背景
我日常用的地图工具(maps_client.py)基于 OpenStreetMap 生态 —— Nominatim 做 geocode/reverse geocode,OSRM 算路径,Overpass 查 POI。这套组合在全球大部分地区够用,但在中国有个硬伤:
Nominatim 的中国地址数据极其稀疏。 输入一组经纬度,返回的往往只有街道名(如”玉清东街”),没有小区名、没有楼栋、没有 landmark。对于 “我在哪个小区附近” 这种日常需求,完全不可用。
方案:接入高德 Web 服务 API
高德开放平台提供 Web 服务 API,免费个人开发者每天 5000 次调用额度。regeo 接口(逆地理编码)直接返回:
- 格式化地址(省市区街道+地标)
- 小区/建筑物名称
- 周边 POI 列表(医院、学校、商场等)
接入成本极低:一个 HTTP GET 请求。
实现
1. 申请 Key
去 高德开放平台控制台 注册个人开发者 → 创建应用 → 添加 Web 服务 API 类型的 Key。不需要绑域名,不需要备案。
Key 是一串 32 位 hex 字符串,像这样:
1 | 5bcc282ebffd771255da0aca935b6636 |
2. API 调用
高德的 regeo 接口极其简单:
1 | GET https://restapi.amap.com/v3/geocode/regeo?key=YOUR_KEY&location=116.3974,39.9042&output=json&extensions=all |
返回示例(北京天安门):
1 | { |
对比 Nominatim 同一位置的返回:
1 | { |
差距明显:Nominatim 只给到街道级,高德给到”人民英雄纪念碑” + 周边 5 个 POI。
3. 代码改动
在 maps_client.py 的 cmd_reverse 函数里,优先尝试 Amap,失败则回退 Nominatim:
1 | AMAP_REVERSE = "https://restapi.amap.com/v3/geocode/regeo" |
完整改动在 maps_client.py v1.3.0。Python stdlib 即可,零外部依赖。
4. 效果对比
青岛市区某点(36.07, 120.38):
Nominatim(之前)
1 | 仅返回街道名,无小区信息 |
Amap/高德地图(之后)
1 | 山东省青岛市市南区香港中路街道华佗路中国人民解放军海军第九七一医院 · 在正一家园附近 |
“在正一家园附近” —— 这才是日常问路需要的精度。
配置方式
设置环境变量即可启用:
1 | export AMAP_KEY="你的32位key" |
也可以写进 shell profile:
1 | echo 'export AMAP_KEY="你的32位key"' >> ~/.zshrc |
之后 maps_client.py reverse 纬度 经度 自动走高德,无需任何其他配置。
踩坑记录
1. Key 类型必须选 Web 服务 API
高德有多个产品线:JS API、Android SDK、iOS SDK、Web 服务 API。如果选了 JS API 或 SDK 类型的 key,调用 REST 接口会返回:
1 | {"status": "0", "info": "USERKEY_PLAT_NOMATCH", "infocode": "10009"} |
解决方法:在控制台重新创建一个”Web 服务 API”类型的 key。
2. 免费的够用
个人开发者免费额度 5000 次/天。对于个人工具使用,完全不可能用完 —— 一天手动查几十次已经很多了。
3. 非中国坐标自动回退
高德只覆盖中国地区。代码里做了 fallback:如果高德请求失败(网络超时、非中国坐标等),自动降级到 Nominatim,不影响海外使用。
总结
- 问题:Nominatim 中国数据稀疏,只能到街道级
- 方案:优先调用高德 Web 服务 API,失败回退 Nominatim
- 成效:小区级地址 + 周边 POI,零外部依赖
- 成本:免费(5000次/天),一行环境变量即可启用