Преобразование 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;
}