Преобразование Lat / Longs в X / Y координаты

У меня есть значение Lat/Long небольшой области в Мельбурне; -37.803134,145.132377,а также плоское изображение того, что я экспортировал из карты openstreet( изображение Osmarender ). Ширина изображения : 1018 и высота:916

Я хотел бы иметь возможность конвертировать, используя C++, Lat/Long в координату X,Y, где точка будет отражать местоположение.

я использовал различные формулы, которые я нашел в интернете, как показано ниже, но ничего не помогает.

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

это будет большая помощь, если кто - нибудь даст мне четкое объяснение того, как это сделать . Любой код был бы очень признателен.

4 ответов


вам нужно больше информации, чем просто одна пара lat/lon, чтобы иметь возможность сделать это.

на данном этапе, информация, которую вы предоставили отсутствует две вещи:

  • насколько велика область вашего изображения (с точки зрения lat/lon)? Основываясь на том, что вы предоставили, я не знаю, показывает ли изображение область шириной метр или километр.
  • к какому месту на вашем изображении относится ваша справочная координата (-37.803134, 145.132377)? Это один из углы? Где-то посередине?

Я также собираюсь предположить, что ваше изображение выровнено по северу/югу - например, у него нет Севера, указывающего на верхний левый угол. Это все усложнит.

самый простой подход-выяснить, какие именно координаты lat/lon соответствуют пикселю (0, 0) и пикселю (1017, 915). Затем вы можете вычислить пиксель, соответствующий заданной координате lat/lon через интерполяция.

чтобы кратко описать этот процесс, представьте, что ваш (-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, чтобы указать второй, и числа между ними, чтобы укажите места между ними. Так, например, он будет производить 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.

EDIT: одно я хотелось бы воспользоваться возможностью, чтобы указать, что это приближение. На самом деле нет простой линейной зависимости между координатами широты и долготы и другими формами декартовых координат по ряду причин, включая проекции, которые используются при составлении карт, и тот факт, что Земля не является идеальной сферой. В малых районах это приближение достаточно близко, чтобы не иметь существенного значения, но в более крупных масштабах расхождения могут стать очевидными. В зависимости от ваших потребностей, YMMV. (Моя благодарность Ураю, чей ответ ниже напомнил мне, что это так).


Если вы ищете точное преобразование геодезического (lot, lan) в определенную декартову координату (X,y метров от опорной точки), вы можете сделать с моим фрагментом кода здесь, эта функция примет геодезическую координату в радиане и выведет результат в x,y

вход:

  • refLat, refLon: геодезическая координата которое вы определили как 0,0 в декартовых координата (единица измерения находится в радиане)
  • lat, lon : геодезический координаты, которые вы хотите к рассчитать ее декартовых координат (блок в Радиан)
  • 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;
}

Я бы не слишком беспокоился о кривизне Земли. Раньше я не использовал openstreetmap, но я просто быстро посмотрел, и кажется, что они используют проекцию Меркатора.

Это просто означает, что они сплющили планету для вас в прямоугольник, сделав X пропорциональным долготе, а Y почти точно пропорциональным широте.

таким образом, вы можете идти вперед и использовать простые формулы Mac, и вы будете очень точны. Ваша широта будет намного меньше тогда пиксель стоит для небольшой карты, с которой Вы имеете дело. Даже на карте размером с Викторию вы получите только ошибку 2-3%.

diverscuba23 указал, что вы должны выбрать эллипсоид... openstreetmap использует WGS84, как и большинство современных карт. Однако будьте осторожны, что многие карты в Австралии используют более старый AGD66, который может отличаться на 100-200 метров или около того.


double testClass::getX(double lon, int width)
{
    // width is map width
    double x = fmod((width*(180+lon)/360), (width +(width/2)));

    return x;
}

double testClass::getY(double lat, int height, int width)
{
    // height and width are map height and width
    double PI = 3.14159265359;
    double latRad = lat*PI/180;

    // get y value
    double mercN = log(tan((PI/4)+(latRad/2)));
    double y     = (height/2)-(width*mercN/(2*PI));
    return y;
}