惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

aimingoo的专栏
aimingoo的专栏
G
Google Developers Blog
B
Blog RSS Feed
A
About on SuperTechFans
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
V
V2EX
Stack Overflow Blog
Stack Overflow Blog
C
Check Point Blog
H
Hackread – Cybersecurity News, Data Breaches, AI and More
Engineering at Meta
Engineering at Meta
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
博客园 - 司徒正美
D
Docker
F
Fortinet All Blogs
Hugging Face - Blog
Hugging Face - Blog
Last Week in AI
Last Week in AI
H
Help Net Security
WordPress大学
WordPress大学
MyScale Blog
MyScale Blog
博客园 - Franky
人人都是产品经理
人人都是产品经理
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
Blog — PlanetScale
Blog — PlanetScale
L
LangChain Blog

又见苍岚

COLMAP PatchMatch Stereo 算法详解 事件驱动的状态机框架:从理论到工程实践 Git 在国内网络环境下无法 Push 的排查与修复 —— 配置 Clash 代理 分段五次多项式插值原理详解 路径插值方法深度对比研究 Claude Code 使用指南 OpenClaw 记忆管理与技能创建指南 CBS(Conflict-Based Search)算法详解 A* 算法及其变种详解 OpenClaw 配置多 Agents Windows Powershell 无法加载文件,因为在此系统上禁止运行脚本问题的解决方案 MaxClaw 安装流程 大模型 AI 名词介绍 AList 网盘聚合工具简介 Protobuf 简介与测试 Claude Code 简介以及 GLM 4.7 模型接入 Github 歌词下载工具 163MusicLyrics Python __getattr__ 懒加载 Python TypedDict 机器人仿真平台 Gazebo 安装记录 机器人仿真平台 Gazebo 简介 多机器人路径规划问题(Multi-Agent Path Finding, MAPF)简介 Python exifread 读取修改过的 jpeg 信息错误问题修复 3D 坐标系变换的理解 3D 旋转矩阵基本概念 MongoDB Compass 介绍 Python 环境管理工具 uv Flutter 开发指南 Snipaste 安装下载与黑屏问题解决方案 全局路径规划算法记录
国内地图瓦片坐标系
Yiwei Zhang · 2025-01-07 · via 又见苍岚

在进行地图开发时,为获取特定经纬度所在区域的瓦片和获取瓦片上像素点对应的经纬度,经常需要进行经纬度坐标与瓦片坐标、像素坐标的相互转换,本文将介绍瓦片坐标相关知识。

瓦片地图

互联网地图服务商的在线地图都通过瓦片的方式提供,称为瓦片地图服务。最常见的地图瓦片是图片格式的,现在有的地图服务商也提供了矢量的瓦片数据,然后在用户端使用Canvas渲染成图片,如node-canvas实现百度地图个性化底图绘制

主要经纬度坐标系

国际标准的经纬度坐标是WGS84,Open Street Map、外国版的Google Map都是采用WGS84;高德地图使用的坐标系是GCJ-02;百度地图使用的坐标系是BD-09。高德地图和百度地图都提供了在线的单向坐标转换接口,将其他坐标系换化到自己的坐标系,但这种转换受限于http url请求字段长度和网络请求延迟,批量处理并不实用。离线相互转换可以通过开源JavaScript库 coordtransform 实现,误差在10米左右。
虽然各地图服务商经纬度坐标系不同,但某一互联网地图的经纬度坐标与瓦片坐标相互转换只与该地图商的墨卡托投影和瓦片编号的定义有关,跟地图商采用的大地坐标系标准无关。

墨卡托投影

墨卡托投影法

瓦片切割和瓦片坐标

对于经过墨卡托投影为平面的世界地图,在不同的地图分辨率(整个世界地图的像素大小)下,通过切割的方式将世界地图划分为像素为$256\times256$的地图单元,划分成的每一块地图单元称为地图瓦片。
地图瓦片具有以下特点:

  • 具有唯一的瓦片等级(Level)和瓦片坐标编号(tileX, tileY)。
  • 瓦片分辨率为 256$\times$256。
  • 最小的地图等级是0,此时世界地图只由一张瓦片组成。
  • 瓦片等级越高,组成世界地图的瓦片数越多,可以展示的地图越详细。
  • 某一瓦片等级地图的瓦片是由低一级的各瓦片切割成的4个瓦片组成,形成了瓦片金字塔。

瓦片切割(瓦片金字塔)

经纬度范围

地理经度的取值范围是 [-180,180],纬度不可能到达90°,通过纬度取值范围为[20037508.3427892,20037508.3427892] ,反计算可得到纬度值为 85.05112877980659。

因此纬度取值范围是[-85.05112877980659,85.05112877980659]。

因此,地理坐标系(经纬度)对应的范围是:最小地理坐标(-180,-85.05112877980659),最大地理坐标(180, 85.05112877980659)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import math

# Given values
y = 20037508.3427892
R = 6378137

# Inverse Mercator projection formula to calculate latitude
# y = R * ln(tan(pi/4 + phi/2))
# Solving for phi
phi = 2 * math.atan(math.exp(y / R)) - math.pi / 2

# Convert the result to degrees
latitude = math.degrees(phi)
print(latitude)
pass

# 输出: 85.05112877980658

地面分辨率(Ground Resolution)

地图以瓦片 (Tile) 方式保存数据,最常用的尺寸是 $256\times256$。

地面分辨率是以一个像素 (pixel) 代表的地面尺寸(米)。以微软 Bing Maps 为例,当 Level 为 1 时,图片大小为$512 \times 512$(4个Tile),那么赤道空间分辨率为:赤道周长/512。其他纬度的空间分辨率则为 纬度圈长度/512。

Level为2时,赤道的空间分辨率为 赤道周长/1024,其他纬度为 纬度圈长度1024。

很明显,Ground Resolution取决于两个参数,缩放级别Level和纬度latitude ,Level决定像素的多少,latitude决定地面距离的长短。

地面分辨率的公式为,单位:米/像素:
$$
\text{ground resolution} = (\cos(latitude * \pi/180) * 2 \pi * 6378137) / (256 * 2 ^\text{ level pixels})
$$
最低地图放大级别(1级),地图是 $512 \times 512$ 像素。每下一个放大级别,地图的高度和宽度分别乘于 2。

2 级是 $1024 \times 1024$像素,3 级是 $2048 \times 2048$ 像素,4 级是 $4096\times 4096$ 像素,等等。

通常而言,地图的宽度和高度可以由以下式子计算得到:
$$
\text{map width }= \text{map height} = 256 \times 2^\text{level pixels}
$$

地图比例尺(Map Scale)

地图比例尺是指测量相同目标时,地图上距离与实际距离的比例。通过地图分辨率在计算可知由Level可得到图片的像素大小,那么需要把其转换为以米为单位的距离,涉及到DPI(dot per inch),暂时可理解为类似的PPI(pixel per inch),即每英寸代表多少个像素。

$256 \times 2 ^{level} / DPI$ 即得到相应的英寸 inch,再把英寸 inch 乘以 0.0254 转换为米。

实地距离仍旧是:$\cos(latitude * \pi/180) * 2 \pi * 6378137 \text{ meters}$; 因此比例尺的公式为:
$$
\text{map scale} = (256 * 2^ {level} / \text{screen dpi} × 0.0254) / (cos(latitude * \pi/180) * 2 \pi * 6378137)
$$
地面分辨率和地图比例尺之间的关系:
$$
\text{map scale} = 1 : \text{ground resolution} * \text{screen dpi} * 0.0254 \text{ meters/inch}
$$

缩放级别 地图宽度、高度(像素) 地面分辨率(米/像素) 地图比例尺(以96dpi为例)
1 512 78,271.5170 1 : 295,829,355.45
2 1,024 39,135.7585 1 : 147,914,677.73
3 2,048 19,567.8792 1 : 73,957,338.86
4 4,096 9,783.9396 1 : 36,978,669.43
5 8,192 4,891.9698 1 : 18,489,334.72
6 16,384 2,445.9849 1 : 9,244,667.36
7 32,768 1,222.9925 1 : 4,622,333.68
8 65,536 611.4962 1 : 2,311,166.84
9 131,072 305.7481 1 : 1,155,583.42
10 262,144 152.8741 1 : 577,791.71
11 524,288 76.4370 1 : 288,895.85
12 1,048,576 38.2185 1 : 144,447.93
13 2,097,152 19.1093 1 : 72,223.96
14 4,194,304 9.5546 1 : 36,111.98
15 8,388,608 4.7773 1 : 18,055.99
16 16,777,216 2.3887 1 : 9,028.00
17 33,554,432 1.1943 1 : 4,514.00
18 67,108,864 0.5972 1 : 2,257.00
19 134,217,728 0.2986 1 : 1,128.50
20 268,435,456 0.1493 1 : 564.25
21 536,870,912 0.0746 1 : 282.12
22 1,073,741,824 0.0373 1 : 141.06
23 2,147,483,648 0.0187 1 : 70.53

像素坐标系和地图图片编码(Bing Maps)

为了优化地图系统性能,提高地图下载和显示速度,所有地图都被分割成 $256 \times 256$ 像素大小的正方形小块。由于在每个放大级别下的像素数量都不一样,因此地图图片(Tile)的数量也不一样。每个tile都有一个XY坐标值,从左上角的 $(0, 0)$ 至右下角的 $(2^{level}–1, 2^{level}–1)$。

例如在3级放大级别下,所有tile的坐标值范围为(0, 0)至(7, 7),如下图:

已知一个像素的XY坐标值时,我们很容易得到这个像素所在的Tile的XY坐标值:

$$ \begin{array}{c} tileX = floor(pixelX / 256) \\ tileY = floor(pixelY / 256) \end{array} $$

为了简化索引和存储地图图片,每个tile的二维XY值被转换成一维字串,即四叉树键值(quardtree key,简称quadkey)。每个quadkey独立对应某个放大级别下的一个tile,并且它可以被用作数据库中B-tree索引值。为了将坐标值转换成quadkey,需要将Y和X坐标二进制值交错组合,并转换成4进制值及对应的字符串。

例如,假设在放大级别为3时,tile的XY坐标值为(3,5),quadkey计算如下:

  • tileX = 3 = 011(二进制)

  • tileY = 5 = 101(二进制)

quadkey = 100111(二进制) = 213(四进制) = “213”

Quadkey 还有其他一些有意思的特性。第一,quadkey 的长度等于该 tile 所对应的放大级别;第二,每个 tile 的 quadkey 的前几位和其父 tile(上一放大级别所对应的tile)的 quadkey 相同,下图中,tile 2是tile 20至23的父tile,tile 13是tile 130至133的父级:

图中每个区域的红色的数字编码,可以记做TileXY。

高德地图瓦片坐标

坐标系定义

**高德地图瓦片坐标与Google Map、Open Street Map相同。**高德地图的墨卡托投影截取了纬度(约85.05ºS, 约85.05ºN)之间部分的地球,使得投影后的平面地图水平方向和垂直方向长度相等。将墨卡托投影地图的左上角作为瓦片坐标系起点,往左方向为X轴,X轴与北纬85.05º重合且方向向左;往下方向为Y轴,Y轴与东经180º(亦为西经180º)重合且方向向下。瓦片坐标最小等级为0级,此时平面地图是一个像素为256*256的瓦片。在某一瓦片层级Level下,瓦片坐标的X轴和Y轴各有$2{Level}$个瓦片编号,瓦片地图上的瓦片总数为$2{Level}\times2^{Level}$。

高德地图 level2 瓦片坐标编号

如上图所示,此时X方向和Y方向各有4个瓦片编号,总瓦片数为16。中国大概位于高德瓦片坐标的(3,1)中。

坐标转换图解

从高德地图坐标转换图解中可以看出,高德地图的坐标转换具有以下特点:

  • 所有坐标转换都在某一瓦片等级下进行,不同瓦片等级下的转换结果不同。
  • 经纬度坐标可以直接转换为瓦片坐标和瓦片像素坐标。
  • 瓦片像素坐标需要结合其瓦片坐标才能得到该像素坐标的经纬度坐标。

坐标转换公式

方法参考:Slippy map tilenames

  • 经纬度坐标(lng, lat)转瓦片坐标(tileX, tileY):

$$
tileX=\lfloor\frac{lng + 180}{360}\times{2^{Level}}\rfloor
$$

$$
tileY=\lfloor{(\frac{1}{2}-\frac{\ln(\tan(lat\times\pi/180)+\sec(lat\times\pi/180))}{2\timesπ}})\times{2^{Level}}\rfloor
$$

  • 经纬度坐标(lng, lat)转像素坐标(pixelX, pixelY):

$$
pixelX=\lfloor\frac{lng + 180}{360}\times{2^{Level}}\times256%256\rfloor
$$

$$
pixelY=\lfloor{(1-\frac{\ln(\tan(lat\times\pi/180)+\sec(lat\times\pi/180))}{2\timesπ}})\times{2^{Level}}\times256%256\rfloor
$$

  • 瓦片(tileX, tileY)的像素坐标(pixelX, pixelY)转经纬度坐标(lng, lat)

$$
lng=\frac{tileX+\frac{pixelX}{256}}{2^{Level}}\times360-180
$$

$$
lat=\arctan({\sinh({\pi-2\times\pi\times\frac{tileY+\frac{pixelY}{256}}{2^{Level}}})})\times\frac{180}{\pi}
$$

百度地图瓦片坐标

坐标系定义

百度地图的瓦片坐标系定义与高德地图并不相同,其墨卡托投影的参数也不同。百度地图瓦片坐标以墨卡托投影地图中赤道与0º经线相交位置为原点,沿着赤道往左方向为X轴,沿着0º经线向上方向为Y轴。
百度瓦片坐标定义了另一种二维坐标系,称为百度平面坐标系。百度平面坐标系的坐标原点与百度瓦片坐标原点相同,以瓦片等级18级为基准,规定18级时百度平面坐标的一个单位等于屏幕上的一个像素。平面坐标与地图所展示的级别没有关系,也就是说在1级和18级下,同一个经纬度坐标的百度平面坐标都是一致的。

此时X方向和Y方向各有4个瓦片编号,但是外围的某些瓦片只有部分区域有地图或完全没有地图。没有地图的区域也可以认为其瓦片是无效的,即百度地图中X方向或Y方向的有效瓦片不一定达到$2^{Level}$个。
中国大概位于百度瓦片坐标的 $(0,0)$ 中。

坐标转换图解

从百度地图坐标转换图解中可以看出,百度地图的坐标转换具有以下特点:

  • 百度经纬度坐标与百度平面坐标可以直接相互转换,并且与瓦片地图等级无关。
  • 经纬度坐标需要先转换为平面坐标,然后才能在某一瓦片等级下转换为瓦片坐标和瓦片像素坐标。
  • 瓦片像素坐标需要结合其瓦片坐标才能得到该像素坐标的平面坐标,然后再转换为经纬度坐标。

坐标转换公式

方法参考:百度地图API详解之地图坐标系统

  • 平面坐标(pointX, pointY)转瓦片坐标(tileX, tileY):

$$
tileX=\lfloor\frac{pointX\times2^{Level-18}}{256}\rfloor
$$

$$
tileY=\lfloor\frac{pointY\times2^{Level-18}}{256}\rfloor
$$

  • 平面坐标(pointX, pointY)转像素坐标(pixelX, pixelY):

$$
pixelX=\lfloor{pointX\times2{Level-18}-\lfloor\frac{pointX\times2{Level-18}}{256}\rfloor\times256}\rfloor
$$

$$
pixelY=\lfloor{pointY\times2{Level-18}-{\lfloor\frac{pointY\times2{Level-18}}{256}\rfloor\times256}}\rfloor
$$

  • 瓦片(tileX, tileY)的像素坐标(pixelX, pixelY)转平面坐标(pointX, pointY):

$$
pointX=\frac{tileX\times256+pixelX}{2^{Level-18}}
$$

$$
pointY=\frac{tileY\times256+pixelY}{2^{Level-18}}
$$

经纬度坐标与瓦片坐标、像素坐标的相互转换,以平面坐标为中间量进行转换。

Web Map 坐标系

对于 Web Map 开发人员来说,最熟悉的应该是EPSG:4326 (WGS84) and EPSG:3857(Pseudo-Mercator)。

EPSG:4326 (WGS84)

前面说了 WGS84 是目前最流行的地理坐标系统。在国际上,每个坐标系统都会被分配一个 EPSG 代码,EPSG:4326 就是 WGS84 的代码。GPS是基于WGS84的,所以通常我们得到的坐标数据都是WGS84的。一般我们在存储数据时,仍然按WGS84存储。

EPSG:3857 (Pseudo-Mercator)

伪墨卡托投影,也被称为球体墨卡托,Web Mercator。它是基于墨卡托投影的,把 WGS84坐标系投影到正方形。我们前面已经知道 WGS84 是基于椭球体的,但是伪墨卡托投影把坐标投影到球体上,这导致两极的失真变大,但是却更容易计算。这也许是为什么被称为”伪“墨卡托吧。另外,伪墨卡托投影还切掉了南北85.051129°纬度以上的地区,以保证整个投影是正方形的。因为墨卡托投影等正形性的特点,在不同层级的图层上物体的形状保持不变,一个正方形可以不断被划分为更多更小的正方形以显示更清晰的细节。很明显,伪墨卡托坐标系是非常显示数据,但是不适合存储数据的,通常我们使用WGS84 存储数据,使用伪墨卡托显示数据。

Web Mercator 最早是由 Google 提出的,当前已经成为 Web Map 的事实标准。但是也许是由于上面”伪“的原因,最初 Web Mercator 被拒绝分配EPSG 代码。于是大家普遍使用 EPSG:900913(Google的数字变形) 的非官方代码来代表它。直到2008年,才被分配了EPSG:3785的代码,但在同一年没多久,又被弃用,重新分配了 EPSG:3857 的正式代码,使用至今。

参考资料

文章链接:
https://www.zywvvd.com/notes/study/gis/gis-map-org/gis-map-org/