面向初次接触本项目的同学。本文解释每种文件是什么、为什么这样存、以及怎么用。
渲染完成后,map/ 目录结构如下:
map/
├── gallery/ ← 城市地图图像(给人看的)
│ ├── Chicago.jpg
│ ├── Tokyo.jpg
│ └── ...(60 张)
│
├── graphs/ ← 城市路网原始数据(给算法用的)
│ ├── Chicago_nodes.parquet
│ ├── Chicago_edges.parquet
│ ├── Tokyo_nodes.parquet
│ ├── Tokyo_edges.parquet
│ └── ...(每座城市 2 个文件,共 120 个)
│
├── cache/ ← OSMnx 的临时 HTTP 缓存(跑完可删)
│ └── ...
│
├── render.log ← 渲染进度日志
└── errors.log ← 失败城市记录
JPG(也写作 JPEG)是最常见的图片格式。相比 PNG,JPG 用有损压缩, 文件体积小很多——对于地图这种"大面积纯色背景"的图像, 肉眼几乎看不出质量差距,但体积能小 5–10 倍。
| 属性 | 值 |
|---|---|
| 分辨率 | 3840 × 2160 像素(4K) |
| 压缩质量 | 92(满分 100,接近无损) |
| 背景色 | #0A0400(近黑深红) |
| 文件大小 | 约 3–10 MB / 张 |
- 直接打开欣赏城市路网形态
- 作为算法动画的背景底图
- 导入 PPT / 海报等设计场景
双击即可,任何图片查看器都支持。
这是本项目最重要的数据,算法模块(A*、Dijkstra 等)就靠它运行。
现实中的城市道路被抽象成数学里的图(Graph):
节点(Node)= 路口 / 转弯点
边 (Edge)= 连接两个节点的路段
举个例子:
[节点 A:某个十字路口] ──── 500米 ──── [节点 B:下一个路口]
↑
这条线就是一条"边"
边上记着:长度 500m、限速 50km/h、单行道否
整座城市的路网就是成千上万个节点和边组成的大网络。
Parquet 是一种专为数据分析设计的列式存储格式, 配合 zstd 压缩算法,是目前压缩比最高、读写最快的开源数据格式之一。
和其他格式的对比(以纽约为例,约 137 万条边):
| 格式 | 文件大小 | 读取速度 | 说明 |
|---|---|---|---|
| GraphML(XML) | ~800 MB | 慢 | 纯文本,极度冗余 |
| GraphML.gz | ~80 MB | 慢 | 压缩后,但读取仍需解析 XML |
| GeoPackage | ~150 MB | 中 | GIS 软件友好 |
| Parquet + zstd | ~20–40 MB | 极快 | 本项目采用,推荐 |
每一行是城市里的一个路口或转弯点。
| 列名 | 类型 | 含义 |
|---|---|---|
osmid |
整数 | OpenStreetMap 节点唯一 ID |
x |
浮点数 | 投影坐标 X(单位:米) |
y |
浮点数 | 投影坐标 Y(单位:米) |
lon |
浮点数 | 经度(WGS84,如 -87.6) |
lat |
浮点数 | 纬度(WGS84,如 41.8) |
geometry |
Point | 节点的地理坐标点对象 |
street_count |
整数 | 该节点连接的道路数量 |
投影坐标 vs 经纬度:
- 经纬度(lon/lat)是球面角度,不能直接算距离
- 投影坐标(x/y)已换算为平面直角坐标,单位是米,可以直接用勾股定理算距离
- A* 算法的启发函数用 x/y 算欧几里得距离,精确且快
每一行是连接两个节点的一段道路。
| 列名 | 类型 | 含义 |
|---|---|---|
u |
整数 | 起点节点的 osmid |
v |
整数 | 终点节点的 osmid |
key |
整数 | 同一对节点间若有多条路,用 key 区分(通常为 0) |
length |
浮点数 | 路段长度(米) |
highway |
字符串 | 道路类型(motorway / primary / residential 等) |
maxspeed |
字符串 | 限速(如 "50",单位 km/h,可能为空) |
oneway |
布尔值 | 是否单行道(True = 只能从 u 走到 v) |
geometry |
LineString | 路段的实际弯曲形状 |
name |
字符串 | 路名(如 "Michigan Avenue",可能为空) |
为什么 length 很重要:
Dijkstra 和 A* 的"边权重"就是用length(找最短距离)
或length / maxspeed(找最短时间)来设置的。
OSMnx 在下载地图数据时,会把从 OpenStreetMap 服务器拿到的原始 HTTP 响应
保存到 cache/ 目录。
作用:如果渲染过程中断,重启时可以直接从缓存读数据, 不用重新联网下载,速度快很多(几秒 vs 几分钟)。
跑完之后:所有城市都渲染完、parquet 都存好之后,
cache/ 目录就没用了,可以直接删掉:
rm -rf map/cache/import geopandas as gpd
nodes = gpd.read_parquet('map/graphs/Chicago_nodes.parquet')
edges = gpd.read_parquet('map/graphs/Chicago_edges.parquet')
print(nodes.head()) # 查看前几行节点
print(edges.head()) # 查看前几行边
print(f"节点数: {len(nodes)}, 边数: {len(edges)}")import osmnx as ox
import geopandas as gpd
nodes = gpd.read_parquet('map/graphs/Chicago_nodes.parquet')
edges = gpd.read_parquet('map/graphs/Chicago_edges.parquet')
# 恢复 MultiIndex(u, v, key)
edges = edges.set_index(['u', 'v', 'key'])
# 还原为 NetworkX 有向图
G = ox.graph_from_gdfs(nodes, edges)
# 然后就可以跑算法了
import networkx as nx
path = nx.shortest_path(G, source=节点A_id, target=节点B_id, weight='length')import pandas as pd
edges = pd.read_parquet('map/graphs/Chicago_edges.parquet')
# 只看边的长度和道路类型
print(edges[['u', 'v', 'length', 'highway']].head(10))所有数据来自 OpenStreetMap(OSM),通过 osmnx 库自动下载。
- OSM 是"地图界的维基百科",全球志愿者共同维护,开源免费
- 数据使用 ODbL 协议(开放数据库许可证),学术和非商业用途无限制
- 不同城市的数据质量有差异:欧美城市覆盖非常完整,非洲部分城市可能有缺失
最后更新:2026-05-30