1. GPS两种格式及转换
常见的GPS数据有两种格式,一种为度(DDD)格式,一种为度分秒(DMS)格式:
- 度分秒(DMS): 118°48′54.152″E 32°04′10.461″N
- 度(DDD): 118.815042°E 32.069572°
两种格式的转换:
- 从DMS 到 DDD,保留度数 D 不变,加上分数 M / 60,再加上秒数 S / 3600;
例如:DMS = 118°48′54.152,DDD=118 + 48/60 + 54.152/3600 = 118.815042。 - 从DDD 到 DMS,保留度数 D 不变(向下取整),小数部分乘以 60 并向下取整为分数,分数剩下的小数部分再乘以 60 得到秒数。
例如:DDD=118.815042,D=118,0.815042*60=48.902520, M=48,S=0.902520*60=54.1512
MATLAB程序如下:
% 将度度度格式转化为度分秒
function [d m s] = ddd2dms(ddd)
d = floor(ddd);
temp = (ddd - d) * 60;
m = floor(temp);
s = (temp - m) * 60;
end
% 将度分秒转化为度度度
function ddd = dms2ddd(d, m, s)
ddd = d + m/60 + s/3600;
end
2. 两点间距离计算
更多信息参考这个链接:http://www.movable-type.co.uk/scripts/latlong-vincenty.html
原文还给出一个网页测试效果,可以编程后与之对比。
公式如下:
其中 :a, b 分别是椭球两点长半轴、短半轴;
fai1, fai2, L 分别是第一点纬度、第二点纬度,两点经度差;
s 是最终计算出的两点距离。
MATLAB程序如下:
% 使用度度度格式计算两点间距离
% 输入:待求两点的坐标(经度1, 纬度1, 经度2, 纬度2)
% 输出:两点间距离
function s = gps_dis(lng1, lat1, lng2, lat2)
a = 6378137.0; % 椭球最大半径(地心到赤道)
b = 6356752.31425; % 椭球最小半径(地心到两极)
f = (a-b) / a; % 椭球扁率(flattening)
fai1 = lat1 * pi /180; % 第一点纬度,要转化为弧度!
fai2 = lat2 * pi /180; % 第二点纬度,要转化为弧度!
L = (lng2 - lng1) * pi / 180; % 两点纬度间弧度差
tanU1 = (1-f) * tan(fai1); % 中间变量,由于多次计算使用,为了提高速度,设置为变量
cosU1 = 1 / sqrt(1 + tanU1^2);
sinU1 = tanU1 * cosU1;
tanU2 = (1-f) * tan(fai2);
cosU2 = 1 / sqrt(1 + tanU2^2);
sinU2 = tanU2 * cosU2;
lamda = L; % 初始化 lamda
for k = 1:100 % 最大允许迭代100次,一般几次就完成了
sin_lamda = sin(lamda);
cos_lamda = cos(lamda);
sinSq_sigma = (cosU2*sin_lamda)^2 + (cosU1*sinU2-sinU1*cosU2*cos_lamda)^2;
sin_sigma = sqrt(sinSq_sigma);
if (sin_sigma == 0)
break;
end
cos_sigma = sinU1*sinU2 + cosU1*cosU2*cos_lamda;
sigma = atan2(sin_sigma, cos_sigma);
sin_alpha = cosU1 * cosU2 * sin_lamda / sin_sigma;
cosSq_alpha = 1 - sin_alpha^2;
cos_2sigmaM = cos_sigma - 2 * sinU1 * sinU2 / cosSq_alpha;
if (isnan(cos_2sigmaM))
cos_2sigmaM = 0;
end
C = f/16 * cosSq_alpha * (4 + f*(4-3*cosSq_alpha));
lamda_2 = lamda;
lamda = L + (1-C) * f * sin_alpha * (sigma + C*sin_sigma* (cos_2sigmaM + C*cos_sigma* (-1 + 2* cos_2sigmaM^2)));
if abs(lamda - lamda_2) < 1e-12
break;
end
end
uSq = cosSq_alpha * (a^2 - b^2) / b^2;
A = 1 + uSq/16384 * (4096 + uSq*(-768 + uSq*(320-175*uSq)));
B = uSq/1024 * (256+uSq*(-128+uSq*(74-47*uSq)));
delta_sigma = B * sin_sigma * (cos_2sigmaM + B/4*(cos_sigma*(-1+2*cos_2sigmaM^2) - B/6*cos_2sigmaM * (-3+4*sin_sigma^2)*(-3+4*cos_2sigmaM^2)));
s = b*A*(sigma-delta_sigma); % 最终距离
alpha1 = atan2(cosU2 * sin(lamda), cosU1 * sinU2 - sinU1 * cosU2 * cos(lamda)); % 起点到终点的方向,弧度制,正北为0,顺时针依次增加到 2*pi
alpha2 = atan2(cosU1 * sin(lamda), (-sinU1) * cosU2 + cosU1 * sinU2 * cos(lamda)); % 终点到起点的方向
end
测试:
(103.000000°, 26.000000°)与(103.000001°, 26.000001°)之间距离:
>> [s alpha1 alpha2] = gps_dis(103, 26, 103.000001, 26.000001)
s = 0.149323101218011
alpha1 = 0.734848600775512
alpha2 = 0.734848608157994
两点间大约 0.15m,夹角为0.7328 rad,大约42°,分辨率高。
C++程序如下:
// GPS距离与方向测量
#include <iostream>
#include <math.h>
using namespace std;
#define pi 3.1415926
/******************************************************************************/
/* 输入: 起始点经纬度(lng1, lat1),终止点经纬度(lng2, lat2) */
/* 输出: 起始点到终止点距离 result[0],起始点与终止点夹角 result[1] */
/* 说明: 正北方向为0°,顺时针旋转增加,采用弧度制 */
/******************************************************************************/
void gps_cacl(double lng1, double lat1, double lng2, double lat2, double* result)
{
double a = 6378137.0; // 椭球最大半径(地心到赤道)
double b = 6356752.31425; // 椭球最小半径(地心到两极)
double f = (a - b) / a; // 椭球扁率(flattening)
double fai1 = lat1 * pi / 180;
double fai2 = lat2 * pi / 180;
double L = (lng2 - lng1) * pi / 180;
double tanU1 = (1 - f) * tan(fai1);
double cosU1 = 1 / sqrt(1 + tanU1 * tanU1);
double sinU1 = tanU1 * cosU1;
double tanU2 = (1 - f) * tan(fai2);
double cosU2 = 1 / sqrt(1 + tanU2 * tanU2);
double sinU2 = tanU2 * cosU2;
double lamda = L;
double sin_lamda;
double cos_lamda;
double sinSq_sigma;
double sin_sigma;
double cos_sigma = 0;
double sigma = 0;
double sin_alpha;
double cosSq_alpha = 0;
double cos_2sigmaM = 0;
double C;
double lamda_2;
int k;
for (k = 0; k < 100; k++)
{
sin_lamda = sin(lamda);
cos_lamda = cos(lamda);
sinSq_sigma = (cosU2 * sin_lamda) * (cosU2 * sin_lamda) + (cosU1 * sinU2 - sinU1 * cosU2 * cos_lamda) * (cosU1 * sinU2 - sinU1 * cosU2 * cos_lamda);
sin_sigma = sqrt(sinSq_sigma);
if (sin_sigma == 0)
break;
cos_sigma = sinU1 * sinU2 + cosU1 * cosU2 * cos_lamda;
sigma = atan2l(sin_sigma, cos_sigma);
sin_alpha = cosU1 * cosU2 * sin_lamda / sin_sigma;
cosSq_alpha = 1 - sin_alpha * sin_alpha;
cos_2sigmaM = cos_sigma - 2 * sinU1 * sinU2 / cosSq_alpha;
if (isnan(cos_2sigmaM))
cos_2sigmaM = 0;
C = f / 16 * cosSq_alpha * (4 + f * (4 - 3 * cosSq_alpha));
lamda_2 = lamda;
lamda = L + (1 - C) * f * sin_alpha * (sigma + C * sin_sigma * (cos_2sigmaM + C * cos_sigma * (-1 + 2 * cos_2sigmaM * cos_2sigmaM)));
if (abs(lamda - lamda_2) < 1e-12)
break;
}
double uSq = cosSq_alpha * (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 delta_sigma = B * sin_sigma * (cos_2sigmaM + B / 4 * (cos_sigma * (-1 + 2 * cos_2sigmaM * cos_2sigmaM) - B / 6 * cos_2sigmaM * (-3 + 4 * sin_sigma * sin_sigma) * (-3 + 4 * cos_2sigmaM *cos_2sigmaM)));
result[0] = b * A * (sigma - delta_sigma);// 最终距离
result[1] = atan2(cosU2 * sin(lamda), cosU1 * sinU2 - sinU1 * cosU2 * cos(lamda));
// result[2] = atan2(cosU1 * sin(lamda), (-sinU1) * cosU2 + cosU1 * sinU2 * cos(lamda));
}
int main()
{
double lng1 = 103.00000, lat1 = 26.00000;
double lng2 = 103.000001, lat2 = 26.000001;
double result[2]; // 结果,包括距离及方向
gps_cacl(lng1, lat1, lng2, lat2, result);
cout << "distance = " << result[0] << endl; // 距离
cout << "direction = " << result[1] << endl; // 方向
return 0;
}
运行结果如下,与MATLAB没什么区别。
distance = 0.149323
direction = 0.734849