Google Maps、Virtual Earth等网络地理所使用的地图投影,常被称作Web Mercator或Spherical Mercator,它与常规墨卡托投影的主要区别就是把地球模拟为球体而非椭球体。建议先对地图投影知识做一个基本的了解,《
地图投影为什么》。
什么是墨卡托投影?
墨卡托(Mercator)投影,又名“等角正轴圆柱投影”,荷兰地图学家墨卡托(Mercator)在1569年拟定,假设地球被围在一个中空的圆柱里,其赤道与圆柱相接触,然后再假想地球中心有一盏灯,把球面上的图形投影到圆柱体上,再把圆柱体展开,这就是一幅标准纬线为零度(即赤道)的“墨卡托投影”绘制出的世界地图。从球到平面,有个转换公式,这里就不再罗列。
Google们为什么选择墨卡托投影?
墨卡托投影的“等角”特性,保证了对象的形状的不变行,正方形的物体投影后不会变为长方形。“等角”也保证了方向和相互位置的正确性,因此在航海和航空中常常应用,而Google们在计算人们查询地物的方向时不会出错。
墨卡托投影的“圆柱”特性,保证了南北(纬线)和东西(经线)都是平行直线,并且相互垂直。而且经线间隔是相同的,纬线间隔从标准纬线(此处是赤道,也可能是其他纬线)向两级逐渐增大。
但是,“等角”不可避免的带来的面积的巨大变形,特别是两极地区,明显的如格陵兰岛比实际面积扩大了N倍。不过要是去两极地区探险或科考的同志们,一般有更详细的资料,不会来查看网络地图的,这个不要紧。
为什么是圆形球体,而非椭球体?
这说来简单,仅仅是由于实现的方便,和计算上的简单,精度理论上差别0.33%之内,特别是比例尺越大,地物更详细的时候,差别基本可以忽略。
经度:这边没问题,可取全球范围:[-180,180]。
纬度:上面已知,纬度不可能到达90°,懒人们为了正方形而取的-20037508.3427892,经过反计算,可得到纬度85.05112877980659。因此纬度取值范围是[-85.05112877980659,85.05112877980659]。其余的地区怎么办?没事,企鹅们不在乎。
因此,地理坐标系(经纬度)对应的范围是:最小(-180,-85.05112877980659),最大(180, 85.05112877980659)。至于其中的Datum、坐标转换等就不再多言。
墨卡托坐标转经纬度
//经纬度转墨卡托
public Vector2D lonLat2Mercator(Vector2D lonLat)
{
Vector2D mercator = new Vector2D();
double x = lonLat.X * 20037508.34 / 180;
double y = Math.Log(Math.Tan((90 + lonLat.Y) * Math.PI / 360)) / (Math.PI / 180);
y = y * 20037508.34 / 180;
mercator.X = x;
mercator.Y = y;
return mercator;
}
//墨卡托转经纬度
public Vector2D Mercator2lonLat(Vector2D mercator)
{
Vector2D lonLat = new Vector2D();
double x = mercator.X / 20037508.34 * 180;
double y = mercator.Y / 20037508.34 * 180;
y = 180 / Math.PI * (2 * Math.Atan(Math.Exp(y * Math.PI / 180)) - Math.PI / 2);
lonLat.X = x;
lonLat.Y = y;
return lonLat;
}
public Vector2D lonLat2Mercator(Vector2D lonLat)
{
Vector2D mercator = new Vector2D();
double x = lonLat.X * 20037508.34 / 180;
double y = Math.Log(Math.Tan((90 + lonLat.Y) * Math.PI / 360)) / (Math.PI / 180);
y = y * 20037508.34 / 180;
mercator.X = x;
mercator.Y = y;
return mercator;
}
//墨卡托转经纬度
public Vector2D Mercator2lonLat(Vector2D mercator)
{
Vector2D lonLat = new Vector2D();
double x = mercator.X / 20037508.34 * 180;
double y = mercator.Y / 20037508.34 * 180;
y = 180 / Math.PI * (2 * Math.Atan(Math.Exp(y * Math.PI / 180)) - Math.PI / 2);
lonLat.X = x;
lonLat.Y = y;
return lonLat;
}