地图坐标系转换详解
- 1. 引言
- 2. 坐标系定义
- 2.1 经纬度坐标系
- 2.2 墨卡托投影坐标系
- 3.3 屏幕坐标系
- 2. 坐标系间的转换
- 2.1 经纬度坐标系到墨卡托投影坐标系
- 2.2 墨卡托投影坐标系到经纬度坐标系
- 2.3 墨卡托投影坐标系到屏幕坐标系
- 2.4 屏幕坐标系到墨卡托投影坐标系
- 2.5 经纬度坐标系到屏幕坐标系
- 2.6 屏幕坐标系到经纬度坐标系
- 3. 示例代码
- 4. 结语
1. 引言
在地理信息系统(GIS)领域,坐标系转换是一项基础而重要的任务。本文将围绕三种主要的地图坐标系——墨卡托投影坐标系、屏幕坐标系和经纬度坐标系——探讨它们之间的相互转换关系,并提供相应的数学推导及实现方法。
2. 坐标系定义
2.1 经纬度坐标系
原点:经纬度坐标系的原点是位于赤道和格林威治子午线交点的位置,即 (0°, 0°)
。
- 定义:经纬度坐标系是最常见的地理坐标系统,其中经度(longitude)范围是
-180°
到+180°
,纬度(latitude)范围是-90°
到+90°
。 - 坐标轴:经度值从西向东递增,纬度值从南向北递增。
- 符号表示:
(lon, lat)
。
North (positive latitude)
|
|
+---+---+
| | |
West (-longitude) +---+---+ East (positive longitude)
| | |
+---+---+
|
|
South (negative latitude)
2.2 墨卡托投影坐标系
原点:墨卡托投影坐标系的原点通常位于 (0°, 0°)
,即赤道和格林威治子午线的交点。但在实际应用中,特别是在Web地图服务中,原点可能被设置为地图的中心点,以适应特定的地图范围。
- 定义:墨卡托投影是一种圆柱投影,地球表面在投影平面上表现为正方形网格,适用于Web地图服务。
- 坐标轴:X 轴代表经度,Y 轴代表纬度,但纬度经过了变形处理。
- 符号表示:
(x, y)
。
谷歌地图、微软地图、百度地图、腾讯地图、高德地图等网络地图所使用的投影都是网络墨卡托投影(Web Mercator),尽管我们喜欢把百度地图、高德地图称之为火星坐标系,不过它们还是没逃出网络墨卡托投影的手心。
North (positive y)
|
|
+---+---+
| | |
West (negative x) +---+---+ East (positive x)
| | |
+---+---+
|
|
South (negative y)
3.3 屏幕坐标系
原点:屏幕坐标系的原点通常位于屏幕的左上角,即 (0, 0)。这意味着 X 值从左向右增大,Y 值从上向下增大。
- 定义:屏幕坐标系是图形显示设备(如显示器)的像素坐标系统。
- 坐标轴:通常情况下,原点位于左上角,X 轴水平向右,Y 轴垂直向下。
- 符号表示:
(x_screen, y_screen)
。
+------------------------------------> x+
|
| (o o)
| / V \
| (_______)
|
|
|
∨
y+
2. 坐标系间的转换
2.1 经纬度坐标系到墨卡托投影坐标系
墨卡托投影的转换公式如下:
x m e r c a t o r = R ⋅ x l o n g i t u d e ⋅ π 180 x_{mercator} = \frac{R \cdot {x_{longitude} } \cdot \pi}{180} xmercator=180R⋅xlongitude⋅π
y m e r c a t o r = R ⋅ ln ( tan ( π 4 + y l a t i t u d e 2 ) ) π 180 y_{mercator} = \frac{R \cdot \ln(\tan(\frac{\pi}{4} + \frac{y_{latitude}}{2})) \pi}{180} ymercator=180R⋅ln(tan(4π+2ylatitude))π
其中,经纬度以角度为单位, R R R是地球半径,通常取为 6378137 米。
2.2 墨卡托投影坐标系到经纬度坐标系
反向转换公式为:
x
l
o
n
g
i
t
u
d
e
=
180
⋅
x
m
e
r
c
a
t
o
r
π
⋅
R
x_{longitude} = \frac{180\cdot x_{mercator}}{\pi\cdot R}
xlongitude=π⋅R180⋅xmercator
y
l
a
t
i
t
u
d
e
=
(
2
⋅
arctan
(
e
y
m
e
r
c
a
t
o
r
/
R
)
−
π
2
)
180
π
y_{latitude} = \frac{(2 \cdot \arctan(e^{y_{mercator}/R}) - \frac{\pi}{2})180}{\pi}
ylatitude=π(2⋅arctan(eymercator/R)−2π)180
2.3 墨卡托投影坐标系到屏幕坐标系
假设屏幕分辨率是 width
和 height
,并且屏幕中心对应于地图的中心点
(
x
c
e
n
t
e
r
,
y
c
e
n
t
e
r
)
(x_{center}, y_{center})
(xcenter,ycenter),缩放级别为 zoomLevel。
计算缩放因子:
s
c
a
l
e
=
2
z
o
o
m
l
e
v
e
l
scale =2^{zoomlevel}
scale=2zoomlevel
计算屏幕中心点对应的墨卡托投影坐标:
x
c
e
n
t
e
r
_
s
r
e
e
n
=
x
c
e
n
t
e
r
_
m
e
r
a
t
o
r
;
y
c
e
n
t
e
r
_
s
r
e
e
n
=
y
c
e
n
t
e
r
_
m
e
r
a
t
o
r
x_{center\_sreen} = x_{center\_merator} ;y_{center\_sreen} = y_{center\_merator}
xcenter_sreen=xcenter_merator;ycenter_sreen=ycenter_merator
计算屏幕坐标:
x
s
c
r
e
e
n
=
(
x
m
e
r
a
t
o
r
−
x
c
e
n
t
e
r
_
m
e
r
a
t
o
r
R
)
⋅
s
c
a
l
e
+
w
i
d
t
h
2
x_{screen} = (\frac{x_{merator}-x_{center\_merator}}{R})\cdot scale +\frac {width}{2}
xscreen=(Rxmerator−xcenter_merator)⋅scale+2width
y
s
c
r
e
e
n
=
h
e
i
g
h
2
−
(
y
m
e
r
a
t
o
r
−
y
c
e
n
t
e
r
_
m
e
r
a
t
o
r
R
)
⋅
s
c
a
l
e
y_{screen} = \frac {heigh}{2}-(\frac{y_{merator}-y_{center\_merator}}{R})\cdot scale
yscreen=2heigh−(Rymerator−ycenter_merator)⋅scale
2.4 屏幕坐标系到墨卡托投影坐标系
假设屏幕坐标为
(
x
s
r
e
e
n
,
y
s
r
e
e
n
)
(x_{sreen},y_{sreen})
(xsreen,ysreen) ,屏幕分辨率为 width 和 height,屏幕中心对应于地图的中心点
(
x
c
e
n
t
e
r
s
r
e
e
n
,
y
c
e
n
t
e
r
s
r
e
e
n
)
(x_{center_sreen},y_{center_sreen})
(xcentersreen,ycentersreen) ,缩放级别为 zoomLevel
计算缩放因子:
s
c
a
l
e
=
2
z
o
o
m
l
e
v
e
l
scale =2^{zoomlevel}
scale=2zoomlevel
计算墨卡托中心点对应的投影屏幕坐标:
x
c
e
n
t
e
r
_
m
e
r
a
t
o
r
=
x
c
e
n
t
e
r
_
s
r
e
e
n
;
y
c
e
n
t
e
r
_
m
e
r
a
t
o
r
=
y
c
e
n
t
e
r
_
s
r
e
e
n
x_{center\_merator} = x_{center\_sreen} ;y_{center\_merator} = y_{center\_sreen}
xcenter_merator=xcenter_sreen;ycenter_merator=ycenter_sreen
计算屏幕坐标:
x
m
e
r
a
t
o
r
=
(
x
s
r
e
e
n
−
s
c
a
l
e
2
)
⋅
R
s
c
a
l
e
+
x
c
e
n
t
e
r
_
m
e
r
a
t
o
r
x_{merator} = (x_{sreen}-\frac{scale}{2})\cdot \frac {R}{scale} + x_{center\_merator}
xmerator=(xsreen−2scale)⋅scaleR+xcenter_merator
y
m
e
r
a
t
o
r
=
y
c
e
n
t
e
r
_
m
e
r
a
t
o
r
−
(
y
s
r
e
e
n
−
s
c
a
l
e
2
)
⋅
R
s
c
a
l
e
y_{merator} = y_{center\_merator} - (y_{sreen}-\frac{scale}{2})\cdot \frac {R}{scale}
ymerator=ycenter_merator−(ysreen−2scale)⋅scaleR
2.5 经纬度坐标系到屏幕坐标系
结合上述 2.1 和 2.3 的转换公式,可以得到:
x
s
c
r
e
e
n
=
(
R
⋅
x
l
o
n
g
i
t
u
d
e
⋅
π
180
−
x
c
e
n
t
e
r
_
m
e
r
a
t
o
r
R
)
⋅
s
c
a
l
e
+
w
i
d
t
h
2
x_{screen} = (\frac{\frac{R \cdot {x_{longitude} } \cdot \pi}{180}-x_{center\_merator}}{R})\cdot scale +\frac {width}{2}
xscreen=(R180R⋅xlongitude⋅π−xcenter_merator)⋅scale+2width
y
s
c
r
e
e
n
=
h
e
i
g
h
2
−
(
R
⋅
ln
(
tan
(
π
4
+
y
l
a
t
i
t
u
d
e
2
)
)
π
180
−
y
c
e
n
t
e
r
_
m
e
r
a
t
o
r
R
)
⋅
s
c
a
l
e
y_{screen} = \frac {heigh}{2}-(\frac{\frac{R \cdot \ln(\tan(\frac{\pi}{4} + \frac{y_{latitude}}{2})) \pi}{180}-y_{center\_merator}}{R})\cdot scale
yscreen=2heigh−(R180R⋅ln(tan(4π+2ylatitude))π−ycenter_merator)⋅scale
2.6 屏幕坐标系到经纬度坐标系
结合上述 2.4 和 2.2 的转换公式,可以得到:
x
l
o
n
g
i
t
u
d
e
=
180
⋅
(
(
x
s
r
e
e
n
−
s
c
a
l
e
2
)
⋅
R
s
c
a
l
e
+
x
c
e
n
t
e
r
_
m
e
r
a
t
o
r
)
π
⋅
R
x_{longitude} = \frac{180\cdot ((x_{sreen}-\frac{scale}{2})\cdot \frac {R}{scale} + x_{center\_merator})}{\pi\cdot R}
xlongitude=π⋅R180⋅((xsreen−2scale)⋅scaleR+xcenter_merator)
y
l
a
t
i
t
u
d
e
=
(
2
⋅
arctan
(
e
y
c
e
n
t
e
r
_
m
e
r
a
t
o
r
−
(
y
s
r
e
e
n
−
s
c
a
l
e
2
)
⋅
R
s
c
a
l
e
R
)
−
π
2
)
180
π
y_{latitude} = \frac{(2 \cdot \arctan(e^{\frac{y_{center\_merator} - (y_{sreen}-\frac{scale}{2})\cdot \frac {R}{scale}}R}) - \frac{\pi}{2})180}{\pi}
ylatitude=π(2⋅arctan(eRycenter_merator−(ysreen−2scale)⋅scaleR)−2π)180
3. 示例代码
以下是一个简单的 C++ 示例代码,演示了上述部分转换的过程:
#include <iostream>
#include <cmath>
// 常量定义
const double EARTH_RADIUS = 6378137.0; // 地球半径,单位:米
const double PI = 3.14159265358979323846;
// 经纬度坐标结构体
struct LatLng {
double lat; // 纬度
double lng; // 经度
};
// 墨卡托投影坐标结构体
struct Mercator {
double x; // 经度方向的投影坐标
double y; // 纬度方向的投影坐标
};
// 屏幕坐标结构体
struct Screen {
double x; // 屏幕x坐标
double y; // 屏幕y坐标
};
// 经纬度转墨卡托投影
Mercator latLngToMercator(const LatLng& latLng) {
Mercator mercator;
mercator.x = latLng.lng * EARTH_RADIUS * PI / 180.0;
mercator.y = log(tan((90.0 + latLng.lat) * PI / 360.0)) * EARTH_RADIUS;
return mercator;
}
// 墨卡托投影转经纬度
LatLng mercatorToLatLng(const Mercator& mercator) {
LatLng latLng;
latLng.lng = mercator.x / (EARTH_RADIUS * PI / 180.0);
latLng.lat = 180.0 / PI * (2.0 * atan(exp(mercator.y / EARTH_RADIUS)) - PI / 2.0);
return latLng;
}
// 墨卡托投影转屏幕坐标
Screen mercatorToScreen(const Mercator& mercator, double zoomLevel) {
Screen screen;
double scale = pow(2.0, zoomLevel);
screen.x = mercator.x * scale / EARTH_RADIUS + 0.5;
screen.y = 0.5 - mercator.y * scale / EARTH_RADIUS;
return screen;
}
// 屏幕坐标转墨卡托投影
Mercator screenToMercator(const Screen& screen, double zoomLevel) {
Mercator mercator;
double scale = pow(2.0, zoomLevel);
mercator.x = (screen.x - 0.5) * EARTH_RADIUS / scale;
mercator.y = (0.5 - screen.y) * EARTH_RADIUS / scale;
return mercator;
}
// 屏幕坐标转经纬度
LatLng screenToLatLng(const Screen& screen, double zoomLevel) {
Mercator mercator = screenToMercator(screen, zoomLevel);
return mercatorToLatLng(mercator);
}
// 经纬度转屏幕坐标
Screen latLngToScreen(const LatLng& latLng, double zoomLevel) {
Mercator mercator = latLngToMercator(latLng);
return mercatorToScreen(mercator, zoomLevel);
}
// 测试代码
int main() {
LatLng latLng = {39.9042, 116.4074}; // 北京经纬度
double zoomLevel = 10.0; // 缩放级别
// 经纬度转墨卡托投影
Mercator mercator = latLngToMercator(latLng);
std::cout << "经纬度转墨卡托投影: (" << mercator.x << ", " << mercator.y << ")" << std::endl;
// 墨卡托投影转经纬度
LatLng latLng2 = mercatorToLatLng(mercator);
std::cout << "墨卡托投影转经纬度: (" << latLng2.lat << ", " << latLng2.lng << ")" << std::endl;
// 墨卡托投影转屏幕坐标
Screen screen = mercatorToScreen(mercator, zoomLevel);
std::cout << "墨卡托投影转屏幕坐标: (" << screen.x << ", " << screen.y << ")" << std::endl;
// 屏幕坐标转墨卡托投影
Mercator mercator2 = screenToMercator(screen, zoomLevel);
std::cout << "屏幕坐标转墨卡托投影: (" << mercator2.x << ", " << mercator2.y << ")" << std::endl;
// 屏幕坐标转经纬度
LatLng latLng3 = screenToLatLng(screen, zoomLevel);
std::cout << "屏幕坐标转经纬度: (" << latLng3.lat << ", " << latLng3.lng << ")" << std::endl;
// 经纬度转屏幕坐标
Screen screen2 = latLngToScreen(latLng, zoomLevel);
std::cout << "经纬度转屏幕坐标: (" << screen2.x << ", " << screen2.y << ")" << std::endl;
return 0;
}
测试效果:
4. 结语
🥳🥳🥳现在,我们在本教程中,您学习了不同坐标系之间的转换涉及到几何变换和数学运算。理解这些转换对于开发地图应用至关重要,希望本文能帮助开发者更好地理解和运用坐标系转换技术。🛹🛹🛹从而实现对外部世界进行感知,充分认识这个有机与无机的环境,后期会持续分享esp32跑freertos实用案列🥳🥳🥳科学地合理地进行创作和发挥效益,然后为人类社会发展贡献一点微薄之力。🤣🤣🤣
如果你有任何问题,可以通过q group(945348278)加入鹏鹏小分队,期待与你思维的碰撞😘😘😘