天地图、OpenStreetMap、本地省市区、混合降级、OpenCage、MapBox、HERE、自建 Nominatim,一套项目全部跑通。
物流配送、上门服务、签到打卡、本地生活……很多业务都绕不开一个基础能力:
把经纬度转换成用户看得懂的地址。
真正接入商业地图服务后,不少团队才发现:免费额度、商用授权、调用量和年费都可能成为长期成本。对中小项目来说,直接采购高价套餐未必是唯一选择。
这个 UniApp 示例项目给出了 8 条可落地路径,并提供 H5、App、小程序可参考的页面与 API 封装。
先说结论
只需要填写收货地址:优先使用省市区三级联动,无需地图 API。 国内业务且需要地图:优先评估天地图。 海外或低频演示:可评估 OpenStreetMap + Nominatim。 线上核心业务:采用主服务 + 备用服务 + 手动录入的混合降级架构。 调用量大、合规和稳定性要求高:使用商业服务,或评估自建 Nominatim。

“免费”不等于没有约束。调用频率、商用条款、数据许可和免费额度都可能调整,上线前请以各服务商最新官方政策为准。
项目实现了什么
四套核心方案
方案一:天地图
项目使用 OpenLayers 加载天地图瓦片,并实现:
经纬度逆地址编码; 影像图、矢量图、地形图切换; 当前坐标标记; H5 地图固定高度 360px; 定位失败后的提示与手动坐标输入。
核心方法:
const result = await tiandituReverseGeocode(116.397470, 39.908823);console.log(result.address);适合国内业务,但需要在天地图开放平台申请自己的 Key,并确认应用类型、域名和服务权限配置正确。
方案二:不调用地图 API,前端完成省市区选择
如果业务只需要用户填写省、市、区和详细地址,并不要求地图选点,那么调用逆地址编码反而增加了成本和失败点。
项目通过 utils/regionData.js 提供前端 JSON 数据,实现:
省份变化后刷新城市; 城市变化后刷新区县; 区县编码与名称同时保存; 不依赖网络和地图服务。
生产项目建议定期更新行政区划数据,并根据数据量决定采用本地 JSON 还是后端接口。
方案三:OpenStreetMap + Nominatim
方案三由两部分组成:
static/osm-map.html:使用 OpenLayers 加载 OSM 瓦片;nominatimReverseGeocode():调用 Nominatim 完成逆地址编码。
const result = await nominatimReverseGeocode(116.397470, 39.908823);console.log(result.address);公共 Nominatim 实例适合低频测试和轻量场景,不应被当作无上限的企业级免费接口。正式业务需要遵守其使用政策,限制频率、提供有效标识,并在高调用量时使用第三方托管服务或自建实例。
方案四:混合降级
更可靠的架构不是“找到一个永不失败的免费接口”,而是让系统在接口失败时仍然可用:
获取坐标 ├─ 主服务成功 → 返回标准化地址 └─ 主服务失败 ├─ 备用服务成功 → 返回地址并记录降级 └─ 备用服务失败 → 展示省市区手动录入项目中的混合页支持自动选择与失败降级,可继续扩展:
超时控制; 熔断与重试; 地址结果缓存; 服务调用量统计; 不同国家或地区的服务路由; 坐标系转换与结果标准化。
其他方案:每个方案都有独立示例
OpenCage
页面: pages/opencage/opencage.vue方法: opencageReverseGeocode(lng, lat)特点:多语言、全球覆盖、接入简单
MapBox
页面: pages/mapbox/mapbox.vue方法: mapboxReverseGeocode(lng, lat)特点:地图样式丰富,适合全球化产品
HERE
页面: pages/here/here.vue方法: hereReverseGeocode(lng, lat, apiKey)特点:企业级地图和位置服务
自建 Nominatim
页面: pages/selfhost/selfhost.vue方法: selfHostedReverseGeocode(lng, lat, baseUrl)特点:服务自主、可控制调用策略,但需要服务器、数据导入、更新和运维能力

30 秒运行项目
1. 使用 HBuilderX 打开
本项目为 UniApp 示例,可直接用 HBuilderX 打开。
2. 配置自己的 Key
编辑 utils/mapApi.js:
const TIANDITU_KEY = 'YOUR_TIANDITU_KEY';const OPENCAGE_KEY = 'YOUR_OPENCAGE_KEY';const MAPBOX_KEY = 'YOUR_MAPBOX_KEY';不要把生产 Key 提交到公开仓库。推荐由后端代理第三方 API,或通过安全配置注入,并设置域名、IP、签名和额度限制。
3. 选择运行平台
H5:运行到浏览器; 微信小程序:运行到微信开发者工具; App:运行到真机或模拟器。
H5 定位为什么经常失败
浏览器定位失败通常不是 UniApp API 本身的问题,而是浏览器安全策略:
除 localhost和127.0.0.1外,通常必须使用 HTTPS;用户拒绝权限后,需要在地址栏左侧的网站权限中重新允许; 桌面设备可能没有 GPS,只能依赖网络定位; VPN、代理、网络环境或系统定位服务可能导致超时; iframe/web-view 还可能受到 Permissions Policy 限制。
调试时建议优先使用:
http://localhost:端口http://127.0.0.1:端口https://你的域名详细排查步骤见 LOCATION_PERMISSION_GUIDE.md。
项目结构
mapApp/├── pages/│ ├── index/ # 方案入口│ ├── tianditu/ # 天地图│ ├── region-select/ # 本地省市区│ ├── openstreetmap/ # OpenStreetMap│ ├── hybrid/ # 混合降级│ ├── other/ # 其他方案入口│ ├── opencage/ # OpenCage 示例│ ├── mapbox/ # MapBox 示例│ ├── here/ # HERE 示例│ └── selfhost/ # 自建 Nominatim 示例├── static/│ ├── tianditu-map.html # OpenLayers + 天地图│ └── osm-map.html # OpenLayers + OSM├── utils/│ ├── mapApi.js # 地图服务 API 封装│ └── regionData.js # 省市区 JSON└── README.md统一调用示例
import { getCurrentLocation, tiandituReverseGeocode, nominatimReverseGeocode, selfHostedReverseGeocode} from'@/utils/mapApi.js';const location = await getCurrentLocation();const address = await tiandituReverseGeocode( location.longitude, location.latitude);console.log(address);上线前必须检查的 7 件事
API Key 是否限制了来源域名、IP 和调用额度; H5 是否部署在 HTTPS 环境; 是否统一处理 WGS84、GCJ-02、BD-09 等坐标系; 第三方接口失败后是否有备用方案; 是否增加缓存、超时、重试、熔断和限流; 是否遵守地图数据许可、署名和商用条款; 是否避免在前端公开可滥用的生产密钥。
怎么选,直接抄这个结论
表单填地址:本地省市区 JSON; 国内地图业务:天地图; 海外低频业务:OpenStreetMap / OpenCage; 需要丰富地图样式:MapBox; 企业海外业务:HERE; 核心线上业务:混合降级; 大调用量且有运维能力:自建 Nominatim。
延伸阅读
写在最后
地图服务选型没有绝对的“免费最优解”,只有与业务规模、数据范围、稳定性和团队能力匹配的方案。
这个项目的价值不只是列出几个 API,而是把地图展示、逆地址编码、手动录入和服务降级放在同一个 UniApp 工程里,方便直接对比和二次开发。
如果这个项目对你有帮助,欢迎收藏、分享,并根据自己的业务继续完善缓存、坐标转换、后端代理和监控能力。
夜雨聆风