干这行最常被问到的需求里,“把这个地点的经纬度打出来”看似是个小活,真做起来能绕进去一整天。最近又要整理一批门店坐标,我顺手把百度地图打印经纬度的几种方式从头到尾捋了一遍,从手动拾取、页面点击取点,到JS API的console输出、批量地理编码,再到纸面打印的排版处理,把踩过的坑也都记了下来。这篇东西没有绕弯子,全是实际操作里能直接照抄的内容,适合前端开发、地图数据整理、门店运营和搞GIS二次开发的同行参考。
1. 这个需求到底在解决什么问题
1.1 一句“打印经纬度”,背后的真实诉求
先说个常被误解的点。“打印经纬度”在很多场景下并不是真的要按Ctrl+P把地图打成纸,而是指把经纬度信息从地图系统中提取出来,以文本、日志、表格或标签的形式输出。我在日常项目里接到过几种典型诉求:
- 前端调试:页面上集成了百度地图,需要点击地图任意位置,立刻在控制台打印出对应的经度、纬度,用来做点位标注和联动查询。
- 运营导数据:外包同学拿了一张Excel,里面只有门店名称和地址,需要在百度地图上找到每个店的位置,再把经纬度填回去,做成一张报表打印给领导看。
- 批量建点:几十个地址要转成经纬度并入数据库,写个定时脚本每天喝一次数据,输出成日志方便排查。
- 纸面归档:工程巡检记录、户外活动路线表、资产管理位置清单,最终要打印在纸质表格上,坐标只是其中一列。
所以第一步就是搞清楚:你要的“打印”到底是控制台输出、页面展示、导出文件还是纸质排版。不同目标对应的工具和代码差别很大,但核心数据只有一个——经纬度。理解了这一点,后面每个方案都能看懂。
1.2 谁需要这份操作清单
我按人群和核心方案整理了一张对应表,方便你在看正文之前直接跳到自己关心的部分。
| 使用人群 | 典型场景 | 推荐方案 |
|---|---|---|
| 前端/地图开发 | 地图点击取点、调试定位 | 百度地图JS API + console,看第3章 |
| 运营/产品 | 把地址转成坐标并导出报表 | 坐标拾取器手动整理,看第2章 |
| 数据/爬虫工程师 | 批量地址转经纬度 | 地理编码API + Python,看第4章 |
| 测绘/GIS方向 | 从链接或二维地图中提取坐标 | 分享链接解析、坐标转换,看第5章和第6章 |
2. 偷懒方案:用坐标拾取器手动取点并打印
2.1 坐标拾取器的正确用法
如果只有一个或几个地点要处理,没必要写代码,直接用百度地图官方的坐标拾取系统就行。入口是百度地图开放平台里的“坐标拾取器”,地址是https://api.map.baidu.com/lbsapi/getpoint/index.html。打开之后页面逻辑很简单:
- 在顶部搜索框输入地点名称,比如“北京西站”。
- 地图定位到该区域后,点击地图上的具体位置,也可以拖动红色气泡微调。
- 页面右侧或底部会实时显示一组坐标,格式通常是“经度,纬度”,比如
116.328154,39.894915。 - 点击坐标旁边的复制按钮,把内容粘贴到记事本、Excel或自己维护的数据库里。
这里有个细节:气泡放在不同位置,坐标完全不同。同一个“北京西站”如果搜索出来的是公交枢纽,你点的是北广场,实际经纬度会差非常多。手动取点一定要放大到能看到目标建筑物轮廓,再慢慢拖动气泡对齐到你要的那个门口。别嫌麻烦,坐标精度不够,后面做门店距离计算、配送范围圈选都会跟着错。
还有一个容易被忽略的点:直接按Ctrl+P打印这个页面,生成出来的纸质文件通常很难看。原因是百度地图瓦片是动态加载的,浏览器打印时不一定会把所有瓦片完整拼出来,可能出现大面积空白或者灰底色。我试过多次,效果都不太稳定。手动方案下,我更建议的做法是把坐标复制出来,单独粘贴到一个文档里再打印,地图截图只是辅助示意。
2.2 把坐标整理成能打印的表格
坐标拿到手之后,规范整理很关键。我在Excel里踩过一个经典坑:经纬度数值位数较多,Excel默认会把超过一定长度的数字转成科学计数法,或者在实际显示时丢掉末尾小数。打印出来的表格里坐标变成一串看不出意义的数字,非常被动。
建议在Excel里这样处理:
- 新建一列,单元格格式直接设为“文本”,再粘贴坐标值,避免被自动转换。
- 如果需要保留数字格式方便计算,设置“数字 → 数值 → 小数位数6位”,超过6位的小数不要随便四舍五入去见人。
- 表头至少包含:地点名称、所属城市、经度、纬度、备注、采集日期。这样打印出来无论是归档还是复核,都一目了然。
表格做好之后,用Excel的“打印预览”检查一下列宽。经纬度如果显示不下,打印出来会被截断,这种情况下把列宽拉伸到12-15,再把页面方向调成横向。要长期归档的话,用“页面布局 → 打印标题”固定表头行,翻页后每一页都有表头,这个操作一定要养成习惯。
3. 技术方案:用百度地图JavaScript API实时获取并打印
3.1 准备工作:申请AK和引入地图库
手动方案始终有上限,一旦地址数量超过50个,或者你需要在业务系统里嵌入地图,就必须走API。我用的比较多的是百度地图JavaScript API,目标就是在页面上点击任意地点,把经纬度实时打印到浏览器控制台。
第一步是去百度地图开放平台注册开发者账号,创建一个“浏览器端”应用,拿到AK(密钥)。创建时有一个Referer白名单配置,开发阶段可以直接填*,但上线前务必改成自己的域名,否则别人拿到你的AK就能盗刷配额。这一步很多人会忽略,结果部署到线上后地图加载不出来,排查半天才发现是白名单没配对。
第二步是引入地图JS文件。目前常用两种版本:
<!-- JS API 3.0,普通版 --> <script type="text/javascript" src="//api.map.baidu.com/api?v=3.0&ak=你的AK"></script> <!-- JS API GL,WebGL版 --> <script type="text/javascript" src="//api.map.baidu.com/api?type=webgl&v=1.0&ak=你的AK"></script>如果是新项目,我建议直接用GL版。它渲染效率更高,视觉上更接近现代地图,事件接口也相对统一。下面示例以GL版为主,但3.0版的event对象结构类似,稍作修改就能复用。
3.2 点击地图拿到经纬度并输出
下面这段代码是一个最小可运行页面:加载地图到指定城市,点击地图时把经度、纬度同时打印到控制台和页面上。
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>点击地图打印经纬度</title> <style> html, body, #map { width: 100%; height: 600px; margin: 0; } #info { padding: 12px 16px; background: #f5f5f5; border: 1px solid #ddd; font-size: 14px; line-height: 1.8; } </style> </head> <body> <div id="map"></div> <div id="info">点击地图,这里会显示经纬度</div> <script src="//api.map.baidu.com/api?type=webgl&v=1.0&ak=你的AK"></script> <script> (function () { var map = new BMapGL.Map("map"); var point = new BMapGL.Point(116.404, 39.915); map.centerAndZoom(point, 15); map.enableScrollWheelZoom(true); map.addEventListener("click", function (e) { var lng = e.latlng.lng; var lat = e.latlng.lat; // 打印到浏览器控制台 console.log("经度: " + lng + ", 纬度: " + lat); // 打印到页面DOM document.getElementById("info").innerHTML = "你点击的位置:经度 <b>" + lng + "</b>,纬度 <b>" + lat + "</b>"; }); })(); </script> </body> </html>这里有一个新手容易踩的坑:BMapGL对象在引入JS文件之后才能用,所以我的代码把脚本标签放在<body>后面,确保地图库已经加载。如果你把API脚本放在<head>里,又在地图库尚未加载完时执行初始化,浏览器会直接报BMapGL is not defined。遇到这个问题,把地图初始化逻辑放到window.onload或页面底部就能解决。
只看坐标还不太够,实际业务中通常还需要知道这个坐标对应的地址。这时候可以配合逆地理编码:点击地图后,用BMapGL.Geocoder把坐标转成地址描述。我常用写法如下:
var geocoder = new BMapGL.Geocoder(); map.addEventListener("click", function (e) { var lng = e.latlng.lng; var lat = e.latlng.lat; console.log("经纬度:", lng, lat); geocoder.getLocation({ lng: lng, lat: lat }, function (result) { if (result && result.addressComponents) { var addr = result.addressComponents; console.log("地址: " + addr.province + addr.city + addr.district + addr.street + addr.streetNumber); document.getElementById("info").innerHTML = "经纬度: " + lng + ", " + lat + "<br>地址: " + addr.province + addr.city + addr.district + addr.street + addr.streetNumber; } }); });注意:逆地理编码属于付费配额接口,频繁点击地图会消耗大量配额,所以我一般会在页面里做节流,比如每次点击后等500毫秒再允许下一次调用。否则并行请求一多,很容易触发并发限制。
3.3 按格式打印到页面和纸张
现在讲回“打印”的另一层含义:把结果区域真正打到纸上。我的做法是页面里放一个结果面板,点击地图时把经纬度写入面板,点击“打印”按钮时只打印结果面板,不打印地图。
<style> .no-print { display: block; } .print-area { display: none; } @media print { .no-print { display: none; } .print-area { display: block; } } </style> <div class="no-print"> <div id="map" style="width:100%;height:500px;"></div> <button onclick="window.print()">打印坐标结果</button> </div> <div class="print-area" id="resultArea"> <h3>地点坐标记录</h3> <p id="printText">请先在地图上点击选择地点</p> </div>在点击事件里,除了更新页面上的#info,同时把内容更新到#printText:
document.getElementById("printText").innerHTML = "经度: " + lng + "<br>纬度: " + lat + "<br>地址: " + addressText;核心逻辑是@media print:打印时隐藏地图,只显示结果区块。这样无论用户怎么点击、地图怎么拖动,最后打印纸上的内容始终干净清爽。如果你遇到打印预览卡顿或闪退,多半是因为地图Canvas面积太大,打印引擎处理不过来,这种“只打印结果区”的方案能直接避开问题。
4. 进阶方案:用地理编码API批量打印地点经纬度
4.1 单个地点怎么调用
如果是几十上百个地址要转成经纬度,再靠手点就太不现实了。这个时候用百度地图Web服务API里的“地理编码”接口,输入地址,返回结构化经纬度。请求参数非常简洁:
curl "https://api.map.baidu.com/geocoding/v3/?address=北京市海淀区上地十街10号&output=json&ak=你的AK"返回的JSON里主要看这几段:
{ "status": 0, "result": { "location": { "lng": 116.308396372, "lat": 40.057035754 }, "precise": 1, "confidence": 80, "comprehension": 100, "level": "门址" } }status为0表示成功,result.location.lng和result.location.lat就是我们要打印的经纬度。这里的坐标默认是百度坐标系(BD-09),后面会专门讲坐标系转换的问题。precise为1代表解析到门址级别,confidence是可信度,批量建点时如果置信度太低,建议单独标记,不要直接进库。
4.2 Python批量打印到终端和CSV
批量处理我通常用Python写一个小脚本,一个地址一个地址地请求,把结果打印到终端的同时写入CSV。代码结构很简单:
import csv import time import requests AK = "你的AK" ADDRESSES = [ "北京市海淀区上地十街10号", "上海市浦东新区世纪大道100号", "广州市天河区体育西路123号", ] def get_location(address: str): url = "https://api.map.baidu.com/geocoding/v3/" params = { "address": address, "output": "json", "ak": AK, } try: resp = requests.get(url, params=params, timeout=5) data = resp.json() except Exception as e: print(f"[请求异常] {address}: {e}") return None, None if data.get("status") == 0: loc = data["result"]["location"] return loc["lng"], loc["lat"] else: print(f"[返回错误] {address}: {data.get('message')}") return None, None def main(): output_file = "locations.csv" with open(output_file, "w", newline="", encoding="utf-8-sig") as f: writer = csv.writer(f) writer.writerow(["地址", "经度", "纬度"]) for addr in ADDRESSES: lng, lat = get_location(addr) if lng is not None and lat is not None: print(f"{addr} -> {lng},{lat}") writer.writerow([addr, lng, lat]) else: writer.writerow([addr, "解析失败", "解析失败"]) # 控制请求频率,避免触发并发限制 time.sleep(0.2) if __name__ == "__main__": main()这里有几个经验点:
- 编码一定要用
utf-8-sig而不是utf-8,否则Office Excel打开CSV时中文会直接乱码。utf-8-sig会写入BOM头,Excel能正确识别UTF-8。 - 每请求一个地址后
sleep(0.2),也就是每秒最多5个请求。个人认证开发者配额有限,别把日志打太快。 - 失败的要单独记录,不要在循环里print完就彻底丢弃。建议把失败地址单独存到一个
failed.csv,下一次脚本重跑只处理失败列表,能省很多配额。
4.3 逆地理编码:拿到经纬度后反查地址
有地址转坐标的需求,自然也有坐标转地址的需求。百度地图提供了/reverse_geocoding/v3/接口,传location=纬度,经度,返回对应的地址描述。这个在“打印地点经纬度信息”的场景里经常配合使用,比如你手里只有一堆GPS坐标,想知道这些点分别落在哪个街道,就需要反查。
curl "https://api.map.baidu.com/reverse_geocoding/v3/?ak=你的AK&location=39.894915,116.328154&output=json"返回的result.formatted_address就是可读地址。我在做外勤轨迹回放时,就是定时把人员上报的经纬度反查成“某路某号”,再打印成日志,方便业务同学快速判断位置是否正常。不过这个接口同样有配额和QPS限制,批量调用时务必控制频率。
5. 从分享链接里提取经纬度并打印
5.1 百度地图分享链接的坐标格式
有些场景连API都不想碰,比如别人在微信里发来一条百度地图的定位分享,你要赶紧把坐标抄下来。这时候不用打开页面再去点,百度地图的分享链接里往往直接带着经纬度。
常见的链接格式大概是:
https://map.baidu.com/@116.404,39.915,18z https://map.baidu.com/?qt=inf&uid=xxx&location=116.404,39.915第一种格式里,@符号后面跟的就是经度、纬度、缩放级别。第二种格式里,location参数就是坐标。解析规则很简单:先找@,再按逗号切分;或者找location=,再按逗号切分。
如果是短链接,比如https://j.map.baidu.com/xxxx,链接里看不到坐标,这种必须先访问短链接拿到跳转后的完整URL,再解析。我在日常工作中碰到的大多数分享都是短链,所以这个前置步骤不能跳过。
5.2 浏览器控制台一行正则打印坐标
如果你正在电脑上打开了百度地图某个位置,想立刻把当前视图中心的经纬度打出来,可以直接按F12打开开发者工具,在Console里执行一段代码:
var url = location.href; var match = url.match(/@([\d.]+),([\d.]+)/) || url.match(/location=([\d.]+),([\d.]+)/); if (match) { var lng = match[1]; var lat = match[2]; console.log("经度: " + lng + ", 纬度: " + lat); }运行后控制台会打印出当前链接里的坐标。这个方法不需要申请任何密钥,纯读链接字段,适合临时救急。
不过要注意,链接里的坐标不一定是你鼠标点击的位置,它多数情况下只是地图视图中心点。如果你要打印的是某个POI(兴趣点)的精确定位,建议还是用坐标拾取器或JS API点击事件,那里拿到的才是你真正标记的位置。
6. 常见问题排查与避坑指南
6.1 坐标对不上?先搞清楚坐标系
这是我在做地图开发时被问得最多的问题。同一个地点,百度地图显示的经纬度,粘到高德地图里偏出去几百米;或者用GPS设备记录的点,放到百度地图上也对不上。原因是三家用的坐标系不一样:
| 坐标系 | 全称 | 使用方 |
|---|---|---|
| BD-09 | 百度坐标系 | 百度地图 |
| GCJ-02 | 火星坐标系 | 高德、腾讯等国内地图 |
| WGS-84 | 世界大地坐标系 | GPS设备、Google Earth、部分GIS软件 |
百度地图默认输出的经纬度是BD-09,如果直接拿到ArcGIS里当WGS-84用,就会出现几百米甚至更大的偏移。做GIS数据处理时尤其要注意。
如果只是把百度坐标转成GCJ-02给高德系用,可以套用下面这个基础转换公式:
import math def bd09_to_gcj02(lng, lat): x_pi = 3.14159265358979324 * 3000.0 / 180.0 x = lng - 0.0065 y = lat - 0.006 z = math.sqrt(x * x + y * y) - 0.00002 * math.sin(y * x_pi) theta = math.atan2(y, x) - 0.000003 * math.cos(x * x_pi) gcj_lng = z * math.cos(theta) gcj_lat = z * math.sin(theta) return gcj_lng, gcj_lat至于GCJ-02转WGS-84,网上能搜到很多公开算法,但精度和适用区域有差异,我一般不直接推荐在生产环境用转换公式。更好的做法是:如果业务对坐标精度要求高,尤其是涉及测绘、无人机、车辆轨迹的专业场景,请直接从官方渠道拿WGS-84数据,或者在采集现场用RTK这类高精度设备做基准点校正。民用展示场景用百度坐标或GCJ-02问题不大,但到了规划、合规层面,必须按项目要求的坐标系来。
6.2 地图不显示、点击没反应、日志打不出来
这一块的问题基本都是环境配置问题,我列出几个高频原因和处理办法:
- 地图白屏:AK没填对,或者AK类型选了服务端,却用在了网页上。浏览器端应用就用浏览器端AK,服务端应用用服务端AK,两者不能混用。
- 页面提示“APP Referer校验失败”:AK的Referer白名单没匹配当前域名。本地调试如果你是用
file://打开的页面,很多浏览器白名单校验会失败,建议本地起个localhost服务调试。 - 点击事件没触发:检查事件绑定是否在地图初始化之后执行。另外要确认你真的注册了
click事件,而不是dblclick,这两个在移动端尤其容易混淆。 - 控制台没有输出:先确认控制台没有过滤掉
log级别,再确认点击时是否真的触发了回调。可以临时在回调里加一个alert测试,能弹窗说明事件回调没问题,问题是console.log被浏览器插件或设置吞了。 - 打印预览有大片空白:地图是用Canvas或瓦片渲染的,打印引擎处理不了,所以尽量让打印区域避开地图本体,只用文本结果排版。
6.3 批量请求失败和配额限制
批量地理编码最常见的报错是QUOTA_LIMIT_EXCEEDED或RATE_LIMIT_EXCEEDED。前者是总配额用完了,后者是并发请求太快。
我的处理经验是:
- 给每个请求加延时,最简单就是
sleep(0.2),QPS基本能压在5以内。 - 请求失败不要立即重试,容易把QPS顶爆。用指数退避,比如第一次失败等1秒,第二次等2秒,第三次等4秒。
- 把成功结果及时落盘,别等全部跑完再统一保存。脚本中途挂了还能从断点继续,不用重新消耗配额。
- 如果日配额不够用,去开发者平台查看“配额调整”入口,按业务需要申请提升。但申请前一定要确认当前AK的配额确实用完了,而不是代码有bug。
6.4 CSV乱码与打印排版问题
CSV乱码的问题前面提过,统一用encoding="utf-8-sig"写入就能解决。但还有一个隐藏问题:如果你用记事本改了CSV再保存成ANSI编码,Excel打开也会乱码。所以维护CSV时,尽量用VS Code这类能识别编码的编辑器,不要反复用系统自带记事本另存为。
纸面打印排版方面,建议表格里的经纬度列宽至少12个字符宽度,字体用等宽字体,比如Consolas或Courier New。等宽字体下,数字和逗号对齐效果非常好,纸质文件扫描后也更容易做OCR识别。
7. 实际使用中的几点补充建议
工具和方法说完了,最后再分享几个我项目里沉淀下来的习惯。
第一,所有坐标数据都要保留采集时间和数据来源。同样一个门店,去年和今年的坐标可能因为地址变更、门牌调整而变化;不同来源拿到的坐标也可能属于不同坐标系。我在数据库里会给每个地点加source字段,标记是百度拾取、API解析还是GPS采集,并且存一份原始坐标系说明。这样哪怕后续发现坐标有偏移,也知道该去哪种口径下排查。
第二,不要重复请求已经拿到的坐标。地理编码接口的配额是花钱的,能本地缓存就本地缓存。我通常会在脚本里维护一个address_cache.json,每次请求前先查本地缓存,命中就直接打印,没命中才调接口。
第三,打印日志要关注异常,而不是只关注坐标。真正在线上跑的时候,我不会只看哪个地址返回了经纬度,而是重点盯status非0的记录、置信度低于某一阈值的记录。这些异常点才是数据质量的主要破坏者。我的脚本里会把所有解析置信度低于60的地址单独输出成一个warning文件,人工复核一遍再入库。
最后说一句实在话:百度地图打印经纬度的需求看起来永远是三五分钟能搞定的小功能,但真正把它做稳定,要考虑坐标系、配额、批量频率、打印排版和环境差异。把上面这些坑提前踩一遍,后面不管接门店数、巡检点还是外勤轨迹,都能少熬夜几次。这篇里的代码和模板我都在自己的项目里跑过,你按场景挑一个用就行,剩下的细节问题欢迎对照各自的报错信息进一步排查。