地理坐标(经纬度坐标)和屏幕坐标(xy坐标)间的转换
在我们的屏幕上,有一张地图,这张地图经过缩放、平移、旋转,最终地理坐标和屏幕坐标的关系大致如下图所示:这种关系要怎么描述呢?我们可以假设地图是一张纸,而屏幕是一堵墙。只要我们有两个图钉,我们就能把纸定在墙上。我们把这两个点称为锚点。锚点在屏幕坐标系上的坐标是(x1,y1)和(x2,y2),对应在地理坐标系上的坐标是(lon1,lat1)和(lon2,lat2)。那现在的问题就变成了,已...
在我们的屏幕上,有一张地图,这张地图经过缩放、平移、旋转,最终地理坐标和屏幕坐标的关系大致如下图所示:
这种关系要怎么描述呢?我们可以假设地图是一张纸,而屏幕是一堵墙。只要我们有两个图钉,我们就能把纸定在墙上。我们把这两个点称为锚点。锚点在屏幕坐标系上的坐标是(x1,y1)和(x2,y2),对应在地理坐标系上的坐标是(lon1,lat1)和(lon2,lat2)。
那现在的问题就变成了,已知两个锚点的坐标,
(1)地理坐标转屏幕坐标:已知任意一点的地理坐标(lon,lat),求它在屏幕上的坐标(x,y)
(2)屏幕坐标转地理坐标:已知任意一点的屏幕坐标(x,y),求它的经纬度坐标(lon,lat)
转换算法
1、地理坐标平面化
首先是地理坐标的平面化转化。在一个小范围内(例如是方圆几公里内),我们可以假设地面是平的,而不是弯的。如果经纬度都用弧度表示,那么1纬度对应的长度是:
1lat_len=R*lat
其中R是地球半径。
而相同经度间的距离会随着纬度的增加而减少,在lat这一纬度下,1经度对应的长度是:
1lon_len=R*lon*cos(lat)
那么,(lon,lat)这个坐标平面化后的坐标就是:(R*lon*cos(lat),R*lat)
2、向量法
由已知点和未知点组成两组向量:
由于坐标系转换是线性变换,所以两组向量有以下特性:
(1)两向量在不同的坐标系中的长度比是相同的。
(2)两向量在不同的坐标系中的夹角是相同的。
根据上面两个特性,我们可列出方程组:
设向量1为(dx1,dy1),(dlon1,dlat1),向量2为(dx2,dy2),(dlon2,dlat2),
其中dx1=x2-x1,dy1=y2-y1,dlon1=lon2-lon1,dlat1=lat2-lat1
dx2=x-x1,dy2=y-y1,dlon2=lon-lon1,dlat2=lat-lat1,
然后k1=norm(dx1,dy1),k2=norm(dlon1,dlat1),k3=norm(dx2,dy2),k4=norm(dlon2,dlat2)
有方程组:
(1)k1/k2 = k3/k4
(2)(dx1*dlon1+dy1*dlat1)/k1/k2 = (dx2*dlon2+dy2*dlat2)/k3/k4
通过解上面的方程组,我们就能得到未知和屏幕坐标或未知的地理坐标。
3、C#代码实现
地理坐标转屏幕坐标:
double lon_cos = Math.Cos(lat2 * Math.PI / 180);
double m = (lon2 - lon1) * lon_cos;
double n = (lat2 - lat1);
double p = (lon - lon1) * lon_cos;
double q = (lat - lat1);
double M = x2 - x1;
double N = y2 - y1;
double a = (p * p + q * q) * (M * M + N * N) / (m * m + n * n);
double b = (m * p + q * n) * norm(M, N) * Math.Sqrt(a) / (norm(m, n) * norm(p, q));
double c = Math.Sqrt(b * b * N * N - (M * M + N * N) * (b * b - a * M * M));
double Q1 = (b * N + c) / (M * M + N * N);
double Q2 = (b * N - c) / (M * M + N * N);
double P1 = (b - Q1 * N) / M;
double P2 = (b - Q2 * N) / M;
double x_1 = P1 + x1;
double y_1 = Q1 + y1;
double x_2 = P2 + x1;
double y_2 = Q2 + y1;
double judge1 = (x_1 - x1) * (y2 - y1) - (y_1 - y1) * (x2 - x1);
double judge2 = (x_2 - x1) * (y2 - y1) - (y_2 - y1) * (x2 - x1);
double judge = (lon - lon1) * (lat2 - lat1) - (lat - lat1) * (lon2 - lon1);
double x = 0;
double y = 0;
if (judge * judge1 < 0)
{
x = x_1;
y = y_1;
}
else
{
x = x_2;
y = y_2;
}
屏幕坐标转地理坐标:
double lon_cos = Math.Cos(lat2 * Math.PI / 180);
double m = (lon2 - lon1) * lon_cos;
double n = (lat2 - lat1);
double M = x2 - x1;
double N = y2 - y1;
double P = x - x1;
double Q = y - y1;
double a = (P * P + Q * Q) * (m * m + n * n) / (M * M + N * N);
double b = (M * P + Q * N) * norm(m, n) * Math.Sqrt(a) / (norm(M, N) * norm(P, Q));
double c = Math.Sqrt(b * b * n * n - (m * m + n * n) * (b * b - a * m * m));
double q1 = (b * n + c) / (m * m + n * n);
double q2 = (b * n - c) / (m * m + n * n);
double p1 = (b - q1 * n) / m;
double p2 = (b - q2 * n) / m;
double lon_1 = p1 / lon_cos + lon1;
double lat_1 = q1 + lat1;
double lon_2 = p2 / lon_cos + lon1;
double lat_2 = q2 + lat1;
double judge1 = (lon_1 - lon1) * (lat2 - lat1) - (lat_1 - lat1) * (lon2 - lon1);
double judge2 = (lon_2 - lon1) * (lat2 - lat1) - (lat_2 - lat1) * (lon2 - lon1);
double judge = (x - x1) * (y2 - y1) - (y - y1) * (x2 - x1);
double lon = 0;
double lat = 0;
if (judge * judge1 < 0)
{
lon = lon_1;
lat = lat_1;
}
else
{
lon = lon_2;
lat = lat_2;
}
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)