将纬度/长度转换为X / Y坐标

时间:2011-02-10 03:48:32

标签: c++ maps coordinates coordinate-systems

我在墨尔本有一个小区域的Lat / Long值; -37.803134,145.132377以及我从openstreet地图(Osmarender Image)导出的平面图像。 图像宽度:1018,高度:916

我希望能够使用C ++将Lat / Long转换为X,Y坐标,其中该点将反映位置。

我使用了我在网上找到的各种公式,如下所示,但没有任何帮助。

var y = ((-1 * lat) + 90) * (MAP_HEIGHT / 180);
var x = (lon + 180) * (MAP_WIDTH / 360);

如果任何人给我清楚解释如何做到这一点将会有很大的帮助。任何代码都会非常感激。

4 个答案:

答案 0 :(得分:24)

您只需要一个纬度/经度对就可以获得更多信息。

在此阶段,您提供的信息缺少两件事:

  • 您的图片覆盖的区域有多大(以纬度/经度计)?根据您提供的内容,我不知道图像是显示一米宽还是一公里宽的区域。
  • 您的图像上的哪个点您的参考坐标(-37.803134,145.132377)是指?这是角落之一吗?在某个地方的中间?

我也会假设你的图像是北/南对齐的 - 例如,它没有指向左上角的北方。这会使事情复杂化。

最简单的方法是确切地确定哪些lat / lon坐标对应于(0,0)像素和(1017,915)像素。然后,您可以通过interpolation找出与给定纬度/经度坐标对应的像素。

要简要概述该过程,假设您的(-37.803134,145.132377)lat / lon对应于您的(0,0)像素,并且您发现您的(1017,915)像素对应于lat / lon(-37.798917,145.138535)。假设左下角有像素(0,0)的常规约定,这意味着图像中的北向上。

然后,如果您对目标坐标(-37.801465,145.134984)感兴趣,可以在图像上找出相应的像素数,如下所示:

pixelY = ((targetLat - minLat) / (maxLat - minLat)) * (maxYPixel - minYPixel)
       = ((-37.801465 - -37.803134) / (-37.798917 - -37.803134)) * (915 - 0)
       = 362.138

即,对应像素距图像底部362像素。然后,您可以对水平像素放置完全相同,但使用经度和X像素。

部分((targetLat - minLat) / (maxLat - minLat))计算两个参考坐标之间的距离,并给出0表示您在第一个参考坐标,1表示您在第一个参考坐标,1表示在第二个参考坐标之间,数字用于表示位于之间。因此,例如,它将产生0.25,表示您在两个参考坐标之间的北方路径的25%。最后一位将其转换为等效像素。

HTH!

编辑好的,根据您的评论,我可以更具体一点。鉴于您似乎使用左上角作为主要参考点,我将使用以下定义:

minLat = -37.803134
maxLat = -37.806232
MAP_HEIGHT = 916

然后,如果我们使用(-37.804465,145.134984)的示例坐标,相应像素的Y坐标相对于左上角是:

pixelY = ((targetLat - minLat) / (maxLat - minLat)) * (MAP_HEIGHT - 1)
       = ((-37.804465 - -37.803134) / (-37.806232 - -37.803134)) * 915
       = 393.11

因此,相应的像素从顶部向下393像素。我会让你自己计算水平等值 - 它基本相同。 注意 -1 MAP_HEIGHT是因为如果从零开始,则最大像素数为915,而非916。

编辑:我想借此机会指出的一点是,这是近似值。实际上,由于多种原因(包括制作地图时使用的投影)以及地球不是完美球体的事实,纬度和经度坐标与其他形式的笛卡尔坐标之间不存在简单的线性关系。在较小的区域,这种近似足够接近,没有显着差异,但在较大的尺度上,差异可能变得明显。根据您的需求,YMMV。 (我要感谢uray,他的答案在下面提醒我,情况确实如此)。

答案 1 :(得分:8)

如果您正在寻找将大地测量(批次,局域网)准确转换为定义的笛卡尔坐标(x,参考点的y米),您可以在此处使用我的代码片段,此函数将接受弧度的大地坐标并输出结果x,y

输入:

  • refLat,refLon:geodetic coordinate 你在笛卡儿中定义为0,0 坐标(单位为弧度)
  • lat,lon:geodetic 你想要的坐标 计算其笛卡尔坐标(单位为弧度)
  • xOffset,yOffset:结果 笛卡尔坐标x,y(单位为米)

代码:

#define GD_semiMajorAxis 6378137.000000
#define GD_TranMercB     6356752.314245
#define GD_geocentF      0.003352810664

void geodeticOffsetInv( double refLat, double refLon,
                        double lat,    double lon, 
                        double& xOffset, double& yOffset )
{
    double a = GD_semiMajorAxis;
    double b = GD_TranMercB;
    double f = GD_geocentF;

    double L     = lon-refLon
    double U1    = atan((1-f) * tan(refLat));
    double U2    = atan((1-f) * tan(lat));
    double sinU1 = sin(U1); 
    double cosU1 = cos(U1);
    double sinU2 = sin(U2);
    double cosU2 = cos(U2);

    double lambda = L;
    double lambdaP;
    double sinSigma;
    double sigma;
    double cosSigma;
    double cosSqAlpha;
    double cos2SigmaM;
    double sinLambda;
    double cosLambda;
    double sinAlpha;
    int iterLimit = 100;
    do {
        sinLambda = sin(lambda);
        cosLambda = cos(lambda);
        sinSigma = sqrt((cosU2*sinLambda) * (cosU2*sinLambda) + 
                        (cosU1*sinU2-sinU1*cosU2*cosLambda) * 
                        (cosU1*sinU2-sinU1*cosU2*cosLambda) );
        if (sinSigma==0)
        {
            xOffset = 0.0;
            yOffset = 0.0;
            return ;  // co-incident points
        }
        cosSigma    = sinU1*sinU2 + cosU1*cosU2*cosLambda;
        sigma       = atan2(sinSigma, cosSigma);
        sinAlpha    = cosU1 * cosU2 * sinLambda / sinSigma;
        cosSqAlpha  = 1 - sinAlpha*sinAlpha;
        cos2SigmaM  = cosSigma - 2*sinU1*sinU2/cosSqAlpha;
        if (cos2SigmaM != cos2SigmaM) //isNaN
        {
            cos2SigmaM = 0;  // equatorial line: cosSqAlpha=0 (§6)
        }
        double C = f/16*cosSqAlpha*(4+f*(4-3*cosSqAlpha));
        lambdaP = lambda;
        lambda = L + (1-C) * f * sinAlpha *
            (sigma + C*sinSigma*(cos2SigmaM+C*cosSigma*(-1+2*cos2SigmaM*cos2SigmaM)));
    } while (fabs(lambda-lambdaP) > 1e-12 && --iterLimit>0);

    if (iterLimit==0)
    {
        xOffset = 0.0;
        yOffset = 0.0;
        return;  // formula failed to converge
    }

    double uSq  = cosSqAlpha * (a*a - b*b) / (b*b);
    double A    = 1 + uSq/16384*(4096+uSq*(-768+uSq*(320-175*uSq)));
    double B    = uSq/1024 * (256+uSq*(-128+uSq*(74-47*uSq)));
    double deltaSigma = B*sinSigma*(cos2SigmaM+B/4*(cosSigma*(-1+2*cos2SigmaM*cos2SigmaM)-
        B/6*cos2SigmaM*(-3+4*sinSigma*sinSigma)*(-3+4*cos2SigmaM*cos2SigmaM)));
    double s = b*A*(sigma-deltaSigma);

    double bearing = atan2(cosU2*sinLambda,  cosU1*sinU2-sinU1*cosU2*cosLambda);
    xOffset = sin(bearing)*s;
    yOffset = cos(bearing)*s;
}

答案 2 :(得分:3)

我不太担心地球曲率。 我之前没有使用openstreetmap,但我只是快速浏览一下,看起来他们使用的是墨卡托投影。

这仅仅意味着他们将你的星球变平为一个矩形,使X与经度成正比,而Y几乎与纬度成正比。

所以你可以继续使用Mac的简单公式,你会非常准确。对于您正在处理的小地图,您的谷歌纵横将远远低于像素值。即使在维多利亚大小的地图上,您也只会得到2-3%的错误。

diverscuba23指出你必须选择一个椭圆体... openstreetmap使用WGS84,大多数现代贴图也是如此。但是,请注意澳大利亚的许多地图使用较旧的AGD66,相差100-200米左右。

答案 3 :(得分:2)

findOne