地理坐标转换 - 地理信息系统(3)

(2017-07-06 银河统计)

在百度地图中,地址“哈尔滨市道里区通达街138号”的经纬度解析为(126.616759, 45.74989),而在高德地图中,相同地址的经纬度则解析为(126.609207, 45.740142)。原因是百度和高德采用的地理坐标系统不同。

本文介绍国内常用网络地图API坐标系统特点,并提供不同坐标间的批量转换解决方案。

一、常用地理坐标体系及分类

地理数据的坐标系一般有两大类,一是地理坐标系(GCS)、是经纬度单位的椭球坐标系;二是投影坐标系(PCS)、是平面直角坐标系。

投影坐标系(PCS)的定义一般会包含两方面的定义信息:

基准面/Datum — 与GCS相应 
投影方法/Projection Method

1、WGS-84原始坐标体系

WGS-84坐标体系又称世界大地坐标系或地球坐标。

国际通用标准,规定GPS 设备中取出的原始数据应该是地球坐标。

“ 世界大地坐标系是美国国防部制图局(Defence Mapping Agency, DMA)为统一世界大地坐标系统,实现全球测量标准的一致性,定义用于制图、大地、导航的坐标基准。它包括标准地球坐标框架、用于处理原始观测数据的标准椭球参考面(即基准和参考椭球)和定义标准海平面的重力等势面(大地水准面)。

定义一个坐标系绝对是一个复杂浩大的数学工程。 我们经常听说的 WGS 1984 (或 WGS 84)就是其中一个世界大地坐标系统。我们经常使用的 GPS 的坐标参考系统也是它。一般用国际标准的GPS记录仪记录下来的坐标,都是GPS的坐标。很可惜,在中国,任何一个地图产品都不允许使用GPS坐标,据说是为了保密。

WGS 1984 是一个长半轴(a)为6378137,短半轴(b)为6356752.314245179 的椭球体,扁率(f)为298.257223563,\(f=\frac{a-b}{a}\)

2、GCJ-02坐标体系

GCJ-02国测局2002年发布的坐标体系,又称“火星坐标”。适用于国内,国内地图供应商基本使用该坐标系对位置进行加密。在中国,很多时候必须使用GCJ-02的坐标体系。比如谷歌,腾讯,高德都在用这个坐标体系。GCJ-02也是国内最广泛使用的坐标体系。

火星坐标系统的原理是这样的:国测局开发了一个系统,能将实际的坐标转换成虚拟的坐标。所有在中国销售的数字地图必须使用这个系统进行坐标转换之后方可上市。这是生产环节,这种电子地图被称为火星地图。在使用环节,GPS终端设备必须集成国测局提供的加密算法,把从GPS卫星那里得到的坐标转换成虚拟坐标,然后再去火星地图上查找,这样就在火星坐标系上完成了地图的匹配。推出这个系统的名义是为了国家安全。然后呢,可以收取一定的费用。

3、其它坐标体系

一般都是由GCJ-02进过偏移算法得到的。这种体系就根据每个公司的不同,坐标体系都不一样了。比如,百度和搜狗就使用自己的坐标体系,与其他坐标体系不兼容。

互联网在线地图使用的坐标系

火星坐标系:

iOS 地图(其实是高德)
Gogole地图
腾讯搜搜地图
阿里云地图
高德地图
搜狐搜狗地图

百度坐标系:

当然只有百度地图

WGS84坐标系(GPS原始坐标系):

国际标准,谷歌国外地图、osm地图等国外的地图

二、地理坐标系与投影原理

地理坐标系(Geographic Coordinate System),是使用三维球面来定义地球表面位置,以实现通过经纬度对地球表面点位引用的坐标系。一个地理坐标系包括角度测量单位、本初子午线和参考椭球体三部分。

地理坐标系为球面坐标。 参考平面地是椭球面,坐标单位为经纬度;投影坐标系为平面坐标。参考平面地是水平面,坐标单位为米、千米等;地理坐标转换到投影坐标的过程可理解为投影。(投影:将不规则的地球曲面转换为平面)。

1、地理坐标

地理坐标系可以确定地球上任何一点的位置。首先将地球抽象成一个规则的逼近原始自然地球表面的椭球体,称为参考椭球体,然后在参考椭球体上定义一系列的经线和纬线构成经纬网,从而达到通过经纬度来描述地表点位的目的。需要说明的是经纬地理坐标系不是平面坐标系,因为度不是标准的长度单位,不可用其直接量测面积长度。

经纬度通常分为天文经纬度、大地经纬度和地心经纬度。常用的经度和纬度是从地心到地球表面上某点的测量角。通常以度或百分度为单位来测量该角度。

在球面系统中,位于两极点中间的纬线称为赤道。它定义的是零纬度线。零经度线称为本初子午线。对于绝大多数地理坐标系,本初子午线是指通过英国格林尼治的经线。其他国家/地区使用通过伯尔尼、波哥大和巴黎的经线作为本初子午线。经纬网的原点 (0,0) 定义在赤道和本初子午线的交点处。这样,地球就被分为了四个地理象限,它们均基于与原点所成的罗盘方位角。南和北分别位于赤道的下方和上方,而西和东分别位于本初子午线的左侧和右侧。

通常,经度和纬度值以十进制度为单位或以度、分和秒 (DMS) 为单位进行测量。纬度值相对于赤道进行测量,其范围是 -90°(南极点)到 +90°(北极点)。经度值相对于本初子午线进行测量。其范围是 -180°(向西行进时)到 180°(向东行进时)。如果本初子午线是格林尼治子午线,则对于位于赤道南部和格林尼治东部的澳大利亚,其经度为正值,纬度为负值。

2、投影坐标

将球面坐标转化为平面坐标的过程便称为投影。

投影坐标系在二维平面中进行定义。与地理坐标系不同,在二维空间范围内,投影坐标系的长度、角度和面积恒定。投影坐标系始终基于地理坐标系,而后者则是基于球体或旋转椭球体的。在投影坐标系中,通过格网上的 x,y 坐标来标识位置,其原点位于格网中心。每个位置均具有两个值,这两个值是相对于该中心位置的坐标。一个指定其水平位置,另一个指定其垂直位置。这两个值称为 x 坐标和 y 坐标。采用此标记法,原点坐标是 x = 0 和 y = 0。

在等间隔水平线和垂直线的格网化网络中,中央水平线称为 x 轴,而中央垂直线称为 y 轴。在 x 和 y 的整个范围内,单位保持不变且间隔相等。原点上方的水平线和原点右侧的垂直线具有正值;下方或左侧的线具有负值。四个象限分别表示正负 X 坐标和 Y 坐标的四种可能组合。在地理坐标系中处理数据时,有时用 X 轴表示经度值并用 Y 轴表示纬度值很有用。

3、墨卡托投影坐标

墨卡托投影,是正轴等角圆柱投影。由荷兰地图学家墨卡托(G.Mercator)于1569年创立。假想一个与地轴方向一致的圆柱切或割于地球,按等角条件,将经纬网投影到圆柱面上,将圆柱面展为平面后,即得本投影。墨卡托投影在切圆柱投影与割圆柱投影中,最早也是最常用的是切圆柱投影。

假设地球被围在一中空的圆柱里,其基准纬线与圆柱相切(赤道)接触,然后再假想地球中心有一盏灯,把球面上的图形投影到圆柱体上,再把圆柱体展开,这就是一幅选定基准纬线上的“墨卡托投影”绘制出的地图。 墨卡托投影没有角度变形,由每一点向各方向的长度比相等,它的经纬线都是平行直线,且相交成直角,经线间隔相等,纬线间隔从基准纬线处向两极逐渐增大。墨卡托投影的地图上长度和面积变形明显,但基准纬线处无变形,从基准纬线处向两极变形逐渐增大,但因为它具有各个方向均等扩大的特性,保持了方向和相互位置关系的正确。在地图上保持方向和角度的正确是墨卡托投影的优点,墨卡托投影地图常用作航海图和航空图,如果循着墨卡托投影图上两点间的直线航行,方向不变可以一直到达目的地,因此它对船舰在航行中定位、确定航向都具有有利条件,给航海者带来很大方便。

墨卡托投影坐标系取零子午线或自定义原点经线(L0)与赤道交点的投影为原点,零子午线或自定义原点经线的投影为纵坐标X轴,赤道的投影为横坐标Y轴,构成墨卡托平面直角坐标系。

三、地理坐标转换

当您获得的经纬度坐标和使用的地理信息系统不一致时,如使用的是百度地图,但经纬度数据却不是百度经纬度,这时必须首先将其它经纬度坐标系转换为百度坐标系,反之亦然。

1、火星坐标转百度坐标

属于火星坐标的地理信息系统主要有高德、Gogole地图、搜搜地图等。

火星坐标: 经度  纬度  
百度坐标: 经度  纬度 
注:输入火星坐标经纬度,点击“坐标转换”按钮,可获得百度经纬度。火星坐标经纬度(126.609207, 45.740142)样例

代码样例

<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="viewport" content="initial-scale=1.0, user-scalable=no" />
<style type="text/css">
    body, html,#allmap {width: 100%;height: 100%;overflow: hidden;margin:0;font-family:"微软雅黑";}
</style>
<script type="text/javascript" src="http://api.map.baidu.com/api?v=2.0&ak=您的密钥"></script>
<title>google转百度</title>
</head>
<body>
<div id="allmap"></div>
</body>
</html>
<script type="text/javascript">
// 百度地图API功能
//谷歌坐标
var x = 116.32715863448607;
var y = 39.990912172420714;
var ggPoint = new BMap.Point(x,y);

//地图初始化
var bm = new BMap.Map("allmap");
bm.centerAndZoom(ggPoint, 15);
bm.addControl(new BMap.NavigationControl());

//添加谷歌marker和label
var markergg = new BMap.Marker(ggPoint);
bm.addOverlay(markergg); //添加谷歌marker
var labelgg = new BMap.Label("未转换的谷歌标注(错误)",{offset:new BMap.Size(20,-10)});
markergg.setLabel(labelgg); //添加谷歌label

//坐标转换完之后的回调函数
translateCallback = function (data){
  if(data.status === 0) {
    var marker = new BMap.Marker(data.points[0]);
    bm.addOverlay(marker);
    var label = new BMap.Label("转换后的百度标注(正确)",{offset:new BMap.Size(20,-10)});
    marker.setLabel(label); //添加百度label
    bm.setCenter(data.points[0]);
  }
}

setTimeout(function(){
    var convertor = new BMap.Convertor();
    var pointArr = [];
    pointArr.push(ggPoint);
    convertor.translate(pointArr, 3, 5, translateCallback)
}, 1000);
</script>
注:代码样例代码摘自百度地图API示例文档

2、GPS原始坐标转百度坐标

原始坐标: 经度  纬度  
百度坐标: 经度  纬度 

注:代码样例和火星坐标转百度坐标代码相同,最后一行代码修改为:
“convertor.translate(pointArr, 1, 5, translateCallback)”

3、火星坐标转和GPS原始坐标批量转百度坐标


火星坐标  原始坐标     

注:按数组格式批量添加经纬度,然后运行“批量坐标转换”按钮。每次最多转换10组经纬度

代码样例

<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="viewport" content="initial-scale=1.0, user-scalable=no" />
<style type="text/css">
    body, html,#allmap {width: 100%;height: 100%;overflow: hidden;margin:0;font-family:"微软雅黑";}
</style>
<script type="text/javascript" src="http://api.map.baidu.com/api?v=2.0&ak=您的密钥"></script>
<title>批量转换</title>
</head>
<body>
<div id="allmap"></div>
</body>
</html>
<script type="text/javascript">

var points = [new BMap.Point(116.3786889372559,39.90762965106183),
              new BMap.Point(116.38632786853032,39.90795884517671),
              new BMap.Point(116.39534009082035,39.907432133833574),
              new BMap.Point(116.40624058825688,39.90789300648029),
              new BMap.Point(116.41413701159672,39.90795884517671)
];
//地图初始化
var bm = new BMap.Map("allmap");
bm.centerAndZoom(new BMap.Point(116.378688937,39.9076296510), 15);
//坐标转换完之后的回调函数
translateCallback = function (data){
  if(data.status === 0) {
    for (var i = 0; i < data.points.length; i++) {
        bm.addOverlay(new BMap.Marker(data.points[i]));
        bm.setCenter(data.points[i]);
    }
  }
}
setTimeout(function(){
    var convertor = new BMap.Convertor();
    convertor.translate(points, 1, 5, translateCallback)
}, 1000);
</script>
注:代码样例代码摘自百度地图API示例文档

4、百度经纬度批量转换为墨卡托坐标


 

在百度地图API中,墨卡托坐标(平面坐标)是以最大级别18级为基准的。在18级条件下,平 面坐标的一个单位就代表了屏幕上的1个像素。平面坐标与地图所展示的级别没有关系,也就是说在1级和18级下,天安门位置的平面坐标都是一致的。

分级地图展示示例如下:

初始化展示中心点经度  纬度   级别 
注:在级别下拉框中选择1-18级观察地图变化效果

5、墨卡托坐标批量转换为百度经纬度


 

百度经纬度转换为摩卡托

var pt = new BMap.Point(116.316163,39.997753);
var projection = map.getMapType().getProjection();
var worldCoordinate = projection.lngLatToPoint(pt);  

worldCoordinate.x和worldCoordinate.y就是平面(墨卡托)坐标的x和y。

摩卡托转换为百度经纬度

var xy = new BMap.Pixel(12948396.92,4837893.73);
var projection = map.getMapType().getProjection();
var LngLat = projection.pointToLngLat(xy);

LngLat.lng和LngLat.lat就是经纬度。

6、百度地图API的像素坐标

平面坐标系的原点与经纬度的原点一致,即赤道与0度经线相交的位置。对于地球上某一个固定的点(如天安门:经度116.403875、纬度39.915168),在不同级别地图图片上该点到平面坐标原点的像素坐标是不同的。在第18级下,直接将平面坐标向下取整就得到了像素坐标,而在其他级别下可以通过如下公式进行换算(这里取整为向下取整):

\[像素坐标 = |平面坐标\times 2^{zoom - 18}| \]

例如,经过计算,在第4级天安门位置的像素坐标是:790, 294。

天安门的经度为116.403875、纬度为39.915168,转换为墨卡托坐标为X=12958161.09、Y=4825948.05。

在第18级地图中,直接将平面坐标向下取整就得到了像素坐标,即(12958161,4825948)。

在第4级地图中,

\[像素坐标X = |12958161.09\times 2^{4 - 18}| = |790.9034|=790 \]

\[像素坐标Y = |4825948.05\times 2^{4 - 18}| = |295.5525|=294 \]

注:计算结果向下取整

7、百度地图API的图块坐标

百度地图API在展示地图时是将整个地图图片切割成若干图块来显示的,当地图初始化或是地图级别、中心点位置发生变化时,地图API会根据当前像素坐标计算出视野内需要的图块坐标(也叫图块编号),从而加载对应的图块用以显示地图。通常,百度地图分辨率越高、显示级别越大,需要的地图图片就越多。

百度地图的图块坐标原点与平面坐标一致,从原点向右上方开始编号为0, 0:

如何知道某个位置的图块坐标呢?通过如下公式计算即可(这里为向下取整):

图块坐标 = |像素坐标 ÷ 256|

256实际上是每个图块的像素宽度和高度,我们用像素坐标除以这个数就知道图块坐标了。

还以天安门为例,在第4级下天安门所在的图块编号为(3,1):

\[图块坐标X = |790 ÷ 256| = |3.086| = 3 (px) \]

\[图块坐标Y = |294 ÷ 256| = |1.1484| = 1 (px) \]

而在第18级下,图块编号为(50617, 18851)

\[图块坐标X = |12958161 ÷ 256| = |50617.82| = 50617 (px) \]

\[图块坐标Y = |4825948 ÷ 256| = |18851.36| = 18851 (px) \]

8、运用Javascript函数进行坐标批量转换

前面给出的国测局坐标(火星坐标,GCJ02)、和GPS坐标(原始坐标,WGS84)转换为百度坐标(BD09)的方法由百度地图API提供。下面的Javascript代码可以更方便地进行不同坐标系的转换。项目细节参考GitHub地址:https://github.com/wandergis/coordtransform。


由  转换为  
注:按数组格式批量添加经纬度,选择相互转换的坐标,然后运行“批量坐标转换”按钮。每次转换数量不受限制,可进行大批量地址转换。

本文提供的各种坐标转换工具仅用于个人学习或教学研究用途,不支持大批量商业用途坐标转换(由于百度地图key的限制)。

posted @ 2017-07-06 21:53  银河统计  阅读(3383)  评论(0编辑  收藏  举报