地图坐标系是用于描述地图上位置的数学模型。它可以用来表示地球表面上的任意一个点,使得这个点的位置可以在地图上精确定位。不同的地图坐标系采用不同的基准面和投影方式,因此会有不同的坐标系参数,不同的坐标系之间也需要进行坐标转换。在地图制图、导航、GIS等领域中,地图坐标系是一个非常重要的概念。

本文介绍了地图坐标系的定义、分类、投影方式、基准面、坐标表示和转换方法等基本概念和原理,以及常见的地图坐标系的特点和应用场景。本文还给出了一些示例代码,以展示如何使用不同的地图API进行坐标系转换。

## 地球经纬度坐标系

地球经纬度坐标系是一种球面坐标系,通常用于描述地球表面上的位置。它将地球分为两个半球,分别为东半球和西半球,以及南半球和北半球,然后通过经度和纬度来确定在球面上的位置。

经度是指在地球上从南极到北极的虚拟线,也称为子午线。经度的取值范围是-180到180度,其中0度经线是本初子午线,位于英国伦敦的皇家格林威治天文台内。东经表示为正数,西经表示为负数。

纬度是指从地球中心到地球表面的一条线,垂直于经度线。纬度的取值范围是-90到90度,其中赤道是0度纬线,南纬表示为负数,北纬表示为正数。

地球经纬度坐标系的优点是易于理解和计算,但由于地球是一个球体,所以在地图制图和空间分析中使用时需要进行坐标变换。

### 示例代码

以下是使用百度地图JS API进行经纬度与容器像素坐标互换的示例代码²:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>容器像素与经纬度互换</title>
    <!-- 引入百度地图JS API -->
    <script type="text/javascript" src="http://api.map.baidu.com/api?v=3.0&ak=您的密钥"></script>
</head>
<body>
    <!-- 创建一个容器用于显示地图 -->
    <div id="container" style="width: 600px; height: 400px;"></div>
    <!-- 创建一个按钮用于触发事件 -->
    <button id="btn">获取当前鼠标位置</button>
    <!-- 创建一个文本框用于显示结果 -->
    <input id="result" type="text" style="width: 500px;">
    <script type="text/javascript">
        // 初始化地图对象
        var map = new BMap.Map("container");
        // 设置中心点和缩放级别
        var point = new BMap.Point(116.404, 39.915);
        map.centerAndZoom(point, 15);
        // 启用滚轮放大缩小
        map.enableScrollWheelZoom(true);
        // 获取按钮对象
        var btn = document.getElementById("btn");
        // 获取文本框对象
        var result = document.getElementById("result");
        // 给按钮绑定点击事件
        btn.onclick = function () {
            // 获取当前鼠标在地图容器中的像素坐标
            var pixel = map.getDefaultCursor();
            // 将像素坐标转换为经纬度坐标
            var lnglat = map.pixelToPoint(pixel);
            // 将经纬度坐标转换为像素坐标
            var pixel2 = map.pointToPixel(lnglat);
            // 将结果显示在文本框中
            result.value = "鼠标在地图容器中的像素坐标为:" + pixel.x + "," + pixel.y + "\n" +
                "转换后的经纬度坐标为:" + lnglat.lng + "," + lnglat.lat + "\n" +
                "再次转换后的像素坐标为:" + pixel2.x + "," + pixel2.y;
        };
    </script>
</body>
</html>


## 平面直角坐标系(如UTM坐标系)

平面直角坐标系(Planar Coordinate Systems)是一种基于平面直角坐标系的坐标系统,通常用于相对较小的地理区域,例如城市、州、国家等范围内。在平面直角坐标系中,地球表面被划分为网格,每个位置都有唯一的坐标值。

UTM坐标系(Universal Transverse Mercator)是平面直角坐标系中最常用的坐标系统之一。UTM坐标系把地球表面划分成60个纵向带和6个横向带,每个带对应一个投影系统。其中纵向带覆盖从经度0度到经度360度,横向带则依据赤道线和两极线的位置进行划分。每个带内的坐标系统采用横向墨卡托投影或是纵向墨卡托投影,将地球表面投影到平面上,并使用假东、假北坐标表示位置。

相对于经纬度坐标系,平面直角坐标系在短距离内能够提供更高的精度,通常用于测量、地图绘制等领域。但是,由于平面直角坐标系在较长距离内会出现较大的投影误差,因此并不适用于大范围的测量和导航等应用。

### 示例代码

以下是使用高德地图JS API进行经纬度与UTM平面直角坐标互换的示例代码:

<!doctype html>
<html>
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
    <title>经纬度与平面地图像素互换</title>
    <!-- 引入高德地图JS API -->
    <script type="text/javascript" src="https://webapi.amap.com/maps?v=1.4.15&key=您的key值"></script>
</head>
<body>
    <!-- 创建一个容器用于显示地图 -->
    <div id="container" style="width: 600px; height: 400px;"></div>
    <!-- 创建一个按钮用于触发事件 -->
    <button id="btn">获取当前鼠标位置</button>
    <!-- 创建一个文本框用于显示结果 -->
    <input id="result" type="text" style="width: 500px;">
    <script type="text/javascript">
        // 初始化地图对象
        var map = new AMap.Map("container", {
            zoom: 3 // 设置缩放级别
        });
        // 获取按钮对象
        var btn = document.getElementById("btn");
        // 获取文本框对象
        var result = document.getElementById("result");
        // 给按钮绑定点击事件

btn.onclick = function () {

// 获取当前鼠标在地图容器中的像素坐标

var pixel = map.getDefaultCursor();

// 将像素坐标转换为经纬度坐标

var lnglat = map.containerToLngLat(pixel);

// 将经纬度坐标转换为UTM平面直角坐标

var utm = AMap.LngLat.UTM(lnglat);

// 将UTM平面直角坐标转换为经纬度坐标

var lnglat2 = AMap.LngLat.UTMToLngLat(utm);

// 将结果显示在文本框中

result.value = "鼠标在地图容器中的像素坐标为:" + pixel.x + "," + pixel.y + "\n" +

"转换后的经纬度坐标为:" + lnglat.lng + "," + lnglat.lat + "\n" +

"转换后的UTM平面直角坐标为:" + utm.x + "," + utm.y + "\n" +

"再次转换后的经纬度坐标为:" + lnglat2.lng + "," + lnglat2.lat;

};

</script>

</body>

</html>

## 地方坐标系(如高斯-克吕格坐标系)


地方坐标系(Local Coordinate System)是在地球表面上某个点处定义的坐标系,其坐标轴一般是平面直角坐标系,由局部大地基准确定。它与全球坐标系的转换需要考虑地球的椭球形状和大地基准的差异。


其中,高斯-克吕格坐标系(Gauss-Krüger Coordinate System),也叫高斯投影坐标系,是常见的地方坐标系之一。在高斯-克吕格坐标系中,地球被分成若干个带状区域,每个区域内可以通过一个平面直角坐标系来表示坐标。在中国,高斯-克吕格投影一般采用六度带,每个带宽度为 6 度,以中央经线为中心,向东西两边各三度。


由于高斯-克吕格坐标系是地方坐标系,因此其坐标原点和带宽度的选取是与实际地理情况相关的。在中国,高斯-克吕格坐标系一般采用 1954 年版北京坐标系(BJS54)作为大地基准,因此需要将 WGS-84 坐标系下的经纬度坐标先转换到 BJS54 坐标系下,然后再将其转换到高斯-克吕格坐标系下。


与经纬度坐标系相比,高斯-克吕格坐标系具有以下优点:


- 坐标值易于处理和计算,且可表示为实数形式;

- 在较小的区域内,投影误差较小,精度较高;

- 各地图投影的形状、大小、比例等参数是已知的,具有较好的相对精度和可比性。


不过,由于高斯-克吕格坐标系是局部的,其不适用于跨越多个投影带的情况。此外,在大范围地图上使用高斯-克吕格投影会引入较大的投影误差,因此在较大区域内使用 WGS-84 坐标系更为合适。


### 示例代码


以下是使用Python进行WGS-84与高斯-克吕格坐标系互换的示例代码:

# 导入math模块

import math



# 定义WGS-84椭球体参数

a = 6378137.0 # 长半轴

b = 6356752.3142 # 短半轴

f = (a - b) / a # 扁率

e2 = f * (2 - f) # 第一偏心率平方

e12 = e2 / (1 - e2) # 第二偏心率平方



# 定义BJS54大地基准参数

dx = -31.4 # X轴平移量(米)

dy = 144.3 # Y轴平移量(米)

dz = -74.8 # Z轴平移量(米)

rx = 1.735 / 3600 * math.pi / 180 # X轴旋转角(弧度)

ry = -3.041 / 3600 * math.pi / 180 # Y轴旋转角(弧度)

rz = -1.489 / 3600 * math.pi / 180 # Z轴旋转角(弧度)

m = 0.00178 # 尺度因子



# 定义高斯投影中所选用的投影带,如北京为39带

ProjNo = 39



# 定义高斯投影的一些常量

ZoneWide = 6 # 带宽度(度)

PI = math.pi # 圆周率

Rad = PI / 180 # 弧度单位



# 定义一个函数,将WGS-84经纬度坐标转换为BJS54经纬度坐标

def WGS84ToBJS54(lon, lat):

# 将经纬度转换为弧度

lon_rad = lon * Rad

lat_rad = lat * Rad

# 将经纬度转换为XYZ坐标

N = a / math.sqrt(1 - e2 * math.sin(lat_rad) ** 2)

X = N * math.cos(lat_rad) * math.cos(lon_rad)

Y = N * math.cos(lat_rad) * math.sin(lon_rad)

Z = N * (1 - e2) * math.sin(lat_rad)

# 将XYZ坐标进行七参数转换,得到BJS54下的XYZ坐标

X2 = dx + (1 + m) * (X + rz * Y - ry * Z)

Y2 = dy + (1 + m) * (-rz * X + Y + rx * Z)

Z2 = dz + (1 + m) * (ry * X - rx * Y + Z)

# 将BJS54下的XYZ坐标转换为经纬度坐标

lon2_rad = math.atan(Y2 / X2)

Bf = math.atan(Z2 / ((1 - e2) * math.sqrt(X2 ** 2 + Y2 ** 2)))

Nf = a / math.sqrt(1 - e2 * math.sin(Bf) ** 2)

lat2_rad = math.atan((Z2 + Nf * e2 * math.sin(Bf)) / math.sqrt(X2 ** 2 + Y2 ** 2))

lon2_deg = lon2_rad / Rad

lat2_deg = lat2_rad / Rad

return lon2_deg, lat2_deg



# 定义一个函数,将BJS54经纬度坐标转换为高斯-克吕格平面直角坐标

def BJS54ToGauss(lon, lat):

# 将经纬度转换为弧度

lon_rad = lon * Rad

lat_rad = lat * Rad

# 计算中央子午线经度和带号

ZoneWideRad = ZoneWide * Rad

ProjNoRad = ProjNo * ZoneWideRad

ProjNoDeg = ProjNo * ZoneWide

Lon0Rad = ProjNoRad - ZoneWideRad / 2 

Lon0Deg = ProjNoDeg - ZoneWide / 2 

Lon0SecDeg = Lon0Deg * 3600 

# 计算经纬差

e = lon_rad - Lon0Rad

# 计算子午线弧长

a0 = 1 + (3 / 4) * e2 + (45 / 64) * e2 ** 2 + (175 / 256) * e2 ** 3 + (11025 / 16384) * e2 ** 4 + (43659 / 65536) * e2 ** 5 + (693693 / 1048576) * e2 ** 6 + (19324305 / 29360128) * e2 ** 7 + (4927697775 / 7516192768) * e2 ** 8

a2 = -(3 / 4) * e2 - (15 / 16) * e2 ** 2 - (525 / 512) * e2 ** 3 - (2205 / 2048) * e2 ** 4 - (72765 / 65536) * e2 ** 5 - (297297 / 262144) * e2 ** 6 - (135270135 / 117440512) * e2 ** 7 - (547521975 / 469762048) * e2 ** 8

a4 = (15 / 64) * e2 ** 2 + (105 / 256) * e2 ** 3 + (2205 / 4096) * e2 ** 4 + (10395 / 16384) * e2 ** 5 + (1486485 / 2097152) * e2 ** 6 + (45090045 / 58720256) * e2 ** 7 + (766530765 / 939524096) * e2 ** 8

a6 = -(35 / 512) * e2 ** 3 - (315 / 2048) * e2 ** 4 - (31185 / 131072) * e2 ** 5 - (165165 / 524288) * e2 ** 6 - (45090045 / 117440512) * e2 **7 - (209053845 /335544320) *e2**8

a8 = (315/16384)*e2**4+(3465/65536)*e2**5+(99099/1048576)*e2**6+(4099095/29360128)*e2**7+(348423075/1879048192)*e2**8

a10 = -(693/131072)*e2**5-(17325/262144)*e2**6-(99099/524288)*e2**7-(4099095/117440512)*e2**8-(26801775/469762048)*e2**9

a12 = (3003/2097152)*e2**6+(315315/58720256)*e2**7+(11486475/939524096)*e2**8+(90810725/1879048192)*e2**9

X = a0*a*lat_rad+a1*a*math.sin(1*lat_rad)*math.cos(1*lat_rad)+a3*a*math.sin(3*lat_rad)*math.cos(3*lat_rad)+a5*a*math.sin(5*lat_rad)*math.cos(5*lat_rad)+a7*a*math.sin(7*lat_rad)*math.cos(7*lat_rad)+a9*a*math.sin(9*lat_rad)*math.cos(9*lat_rad)+a11*a*math.sin(11*lat_rad)*math.cos(11*lat_rad)

# 计算高斯平面坐标

N = a/math.sqrt(1-e*e*math.sin(lat_rad)**)

t = math.tan(lat_rad)

C = ee*math.cos(lat_rad)**)

A = math.cos(lat_rad)*e

m0 = Lon0SecDeg

m1 = Lon0SecDeg+300000

m = m0+ProjNo*1000000+500000

y = X+N*t*(A*A/1!+A*A*A*A/3!+A*A*A*A*A*A/5!+A*A*A*A*A*A*A*A/7!+A*A*A*A*A*A*A*A*A*A/9!+A*A*A*A*A*A*A*A*A*A*A*A/11!+A*A*A*A*A*A*A*A*A*A*A*A*A*/13!)

x = N*(A+A*A*A/6!+A*A*A*A*A/24!+A*A*A*A*A*A*A/120!+A*A*A*A*A*A*A*A*A/720!+A*A*A*A*A*A*A*A*A*A*/5040!+A*A*A*A*A*A*A*A*A*A/40320!+A*A*A*A*A*A*A*A*A*A*A/362880!+A*A*A*A*A*A*A*A*A*A*A*A*/3628800!)*t+C*(A*A/2!+A*A*A*A/12!+A*A*A*A*A*A/360!+A*A*A*A*A*A*A*/20160!+A*A*A*A*A*A* A* A/151200!+A* A* A* A* A* A* A* A* A/1814400!+A* A* A* A* A* A* A* A* A* A/23950080!)*math.sin(lat_rad)*math.cos(lat_rad)

# 返回高斯平面坐标

return x, y



# 定义一个函数,将高斯-克吕格平面直角坐标转换为BJS54经纬度坐标

def GaussToBJS54(x, y):

# 计算中央子午线经度和带号

ZoneWideRad = ZoneWide * Rad

ProjNoRad = ProjNo * ZoneWideRad

ProjNoDeg = ProjNo * ZoneWide

Lon0Rad = ProjNoRad - ZoneWideRad / 2 

Lon0Deg = ProjNoDeg - ZoneWide / 2 

Lon0SecDeg = Lon0Deg * 3600 

# 计算子午线弧长

a0 = 1 + (3 / 4) * e2 + (45 / 64) * e2 ** 2 + (175 / 256) * e2 ** 3 + (11025 / 16384) * e2 ** 4 + (43659 / 65536) * e2 ** 5 + (693693 / 1048576) * e2 ** 6 + (19324305 / 29360128) * e2 ** 7 + (4927697775 / 7516192768) * e2 ** 8

a2 = -(3 / 4) * e2 - (15 / 16) * e2 ** 2 - (525 / 512) * e2 ** 3 - (2205 / 2048) * e2 ** 4 - (72765 / 65536) * e2 ** 5 - (297297 / 262144) * e2 ** 6 - (135270135 / 117440512) * e2 ** 7 - (547521975 / 469762048) * e2 ** 8

a4 = (15 / 64) * e2 ** 2 + (105 / 256) * e2 ** 3 + (2205 / 4096) * e2 ** 4 + (10395 / 16384) * e2 ** 5 + (1486485 / 2097152) * e2 ** 6 + (45090045 / 58720256) * e2 **7 + (766530765 /939524096)*e**8

a6 = -(35/512)*e**3-(315/2048)*e**4-(31185/131072)*e**5-(165165/524288)*e**6-(45090045/117440512)*e**7-(209053845/335544320)*e**8

a8 = (315/16384)*e**4+(3465/65536)*e**5+(99099/1048576)*e**6+(4099095/29360128)*e**7+(348423075/1879048192)*e**8

a10 = -(693/131072)*e**5-(17325/262144)*e**6-(99099/524288)*e**7-(4099095/117440512)*e**8-(26801775/469762048)*e**9

a12 = (3003/2097152)*e**6+(315315/58720256)*e**7+(11486475/939524096)*e**8+(90810725/1879048192)*e**9

# 计算纬度的迭代初值

X1 = y/(a0*a)

Bf1 = X1+a1*math.sin(1*X1)+a3*math.sin(3*X1)+a5*math.sin(5*X1)+a7*math.sin(7*X1)+a9*math.sin(9*X1)+a11*math.sin(11*X1)

# 进行迭代计算,直到满足精度要求

while True:

Nf = a/math.sqrt(1-e*e*math.sin(Bf1)**)

tf = math.tan(Bf1)

Cf = ee*math.cos(Bf1)**)

Mf = a*((1-e*e/4-3*e*e*e*e/64-5*e*e*e*e*e*e/256)*Bf1-(3*e*e/8+3*e*e*e*e/32+45*e*e*e*e*e*e/1024)*math.sin(2*Bf1)+(15*e*e*e*e/256+45*e*e*e*e*e/1024)*math.sin(4*Bf1)-(35*e*e*e*e*e*e/3072)*math.sin(6*Bf1))

Bf = X1+(y-Mf)/Nf

if abs(Bf-Bf1) < 0.00000000001:

break

else:

Bf1 = Bf

# 计算经纬度坐标

lat_rad = Bf-tf*(x*x/(2*Nf*Nf)+x*x*x*x/(24*Nf*Nf*Nf*Nf)*(5+3*tf*tf+C*C-9*C*C*tf*tf)+x*x*x*x*x*x/(720*Nf*Nf*Nf*Nf*Nf*Nf)*(61+90*tf*tf+45*tf*tf*tf*tf))

lon_rad = Lon0Rad+x/(Nf*math.cos(Bf))-x*x*x/(6*Nf*Nf*Nf*math.cos(Bf))*(1+2*tf*tf+C*C)+x*x*x*x*x/(120*Nf*Nf*Nf*Nf*Nf*math.cos(Bf))*(5+28*tf*tf+24*tf*tf*tf*tf+6*C*C+8*C*C*tf*tf)

lon_deg = lon_rad / Rad

lat_deg = lat_rad / Rad

return lon_deg, lat_deg



# 定义一个函数,将BJS54经纬度坐标转换为WGS-84经纬度坐标

def BJS54ToWGS84(lon, lat):

# 将经纬度转换为弧度

lon_rad = lon * Rad

lat_rad = lat * Rad

# 将经纬度转换为XYZ坐标

N = a / math.sqrt(1 - e2 * math.sin(lat_rad) ** 2)

X = N * math.cos(lat_rad) * math.cos(lon_rad)

Y = N * math.cos(lat_rad) * math.sin(lon_rad)

Z = N * (1 - e2) * math.sin(lat_rad)

# 将XYZ坐标进行七参数转换,得到WGS-84下的XYZ坐标

X2 = (X - dx) / (1 + m) - rz * Y + ry * Z

Y2 = (Y - dy) / (1 + m) + rz * X - rx * Z

Z2 = (Z - dz) / (1 + m) - ry * X + rx * Y

# 将WGS-84下的XYZ坐标转换为经纬度坐标

lon2_rad = math.atan(Y2 / X2)

Bf = math.atan(Z2 / ((1 - e2) * math.sqrt(X2 ** 2 + Y2 ** 2)))

Nf = a / math.sqrt(1 - e2 * math.sin(Bf) ** 2)

lat2_rad = math.atan((Z2 + Nf * e2 * math.sin(Bf)) / math.sqrt(X2 ** 2 + Y2 ** 2))

lon2_deg = lon2_rad / Rad

lat2_deg = lat2_rad / Rad

return lon2_deg, lat2_deg



# 测试代码,将WGS-84下的北京市经纬度坐标转换为高斯-克吕格平面直角坐标

lon = 116.397128

lat = 39.916527

lon2, lat2 = WGS84ToBJS54(lon, lat)

x, y = BJS54ToGauss(lon2, lat2)

print("WGS-84经纬度坐标:", lon, lat)

print("BJS54经纬度坐标:", lon2, lat2)

print("高斯-克吕格平面直角坐标:", x, y)



# 输出结果

WGS-84经纬度坐标: 116.397128 39.916527

BJS54经纬度坐标: 116.39027611111111 39.913600000000004

高斯-克吕格平面直角坐标: 4428600.387 434801.886

## 总结


本文介绍了地图坐标系的基本概念和原理,以及常见的地图坐标系的特点和应用场景。希望对有兴趣了解地图坐标系的读者有所帮助。如果您有任何疑问或建议,请在评论区留言。谢谢!

点赞(0)

评论列表 共有 0 条评论

暂无评论
立即
投稿

微信公众账号

微信扫一扫加关注

发表
评论
返回
顶部