北京地铁足迹地图记录工具已经上线并开源了。它解决的是一个很小但很具体的问题记录你坐过北京地铁的哪些站点然后把这张“足迹地图”可视化出来。如果你喜欢记录城市出行或者正想找一个能练手地图可视化、数据处理、部署上线和开源发布全链路的个人项目这个方向的完整链路很值得拆一遍。最值得关注的地方不是它看起来多炫而是从站点数据到地图渲染、从打卡逻辑到统计展示每一步都不算难但合在一起需要认真处理。下面我按实际落地顺序拆开讲。先说这个工具到底在记录什么再讲功能怎么拆分、技术方案怎么选、实操流程怎么走最后聊开源和踩坑。如果你想自己复刻一个类似项目可以参考思路不用照搬我的代码。1. 足迹地图工具到底帮你记录什么1.1 表面是打卡本质是站点状态的数据维护很多人第一次看到“足迹地图”四个字会以为它像一个导航软件自动记录你到过的每一个地方。实际做下来会发现它的核心不是定位而是维护一份“站点是否已经被乘坐过”的数据集合。比如我从西直门坐到国贸中间经过车公庄、平安里、北海北、南锣鼓巷、东四、朝阳门最后到国贸。如果只记录起点和终点就没有完整表达这次行程如果记录全部途经站点又需要线路拓扑数据和按站识别能力。这个工具最基础的用法是手动点击站点把坐过的站点标记为“已完成”。点击那一刻本质上是在更新一个集合把该站点的 ID 加入已乘坐列表。所以它更像一个“地铁站打卡本”而不是导航或实时位置记录工具。1.2 适合谁不适合谁适合下面这几类人地铁迷。想统计自己刷了多少条线路、多少座车站还差哪些站。通勤党。每天固定路线想看看一年下来哪些站反复出现。带娃家庭。周末坐地铁去城市各处探索边玩边记录。开发者。想学地图可视化、数据持久化、开源项目发布流程。不适合的场景也要说清楚如果你需要实时导航、换乘方案、到站提醒应该用成熟地图 App。如果你希望“手机放口袋里就自动生成足迹”需要接入定位和后台轨迹记录复杂度会高很多不是这个项目的优先目标。如果你需要企业级地图服务、海量用户高并发那还要做账号、权限、数据库和服务端架构。一句话总结这是一个轻量、个人向、以手动打卡为基础的地铁足迹记录工具。它把一个想法做到了完整闭环而不是做成一个大而全的地图平台。2. 功能拆分先想清楚最小能跑的版本2.1 最小闭环先让足迹能“点亮”做个人项目最怕一上来就规划特别多功能。我的建议是先列出一个最小可用闭环跑通之后再扩展。对于一个北京地铁足迹地图记录工具最小闭环可以是展示一张北京地铁地图能看清站点和线路。每个站点可以点击点击后切换为“已乘坐”状态。已乘坐的站点在地图上有明显高亮比如改变颜色。有一个简单的统计当前已乘坐站点数、总站点数、覆盖率百分比。数据能被保存刷新页面后不丢失。能做到这五条工具就已经能用了。我自己在实际开发时也是先完成了“点亮站点”这个核心交互才继续加统计和分享功能。2.2 进阶能力刷线进度、分享卡片、数据导出最小闭环稳定之后再考虑更实用的功能。首先是“按线路统计”。北京地铁有很多条线路有的线路我只坐过其中几站。按线路维度展示“已乘站数 / 总站数”会比只看总数更直观。比如“10号线已完成 32/45”这种进度感是足迹类工具最容易吸引用户的地方。其次是“分享卡片”。把当前足迹地图做成一张图片方便发到朋友圈或群里。实现的思路是用 Canvas 把地图截图和统计数据绘制成一张固定尺寸的图片。注意如果直接截取第三方地图瓦片需要确认是否允许这种使用方式更稳妥的做法是只截取自己绘制的高亮站点和统计区域或者用纯白色的自定义背景重新绘制一份“足迹卡片”。然后是“数据导入导出”。个人工具最怕换设备或清理浏览器后数据丢失。提供一个导出 JSON 文件的功能再把数据备份到本地体验会好很多。这个功能实现起来很简单但对长期使用非常有帮助。2.3 数据边界哪些数据不碰做一个地图相关工具容易掉进“什么数据都想要”的坑。我自己的建议是明确数据边界不做实时定位采集。只在用户手动点击站点时记录站点 ID不获取手机位置。不做实时地铁运营数据。车次、拥堵、延误这些数据需要官方或特殊数据源维护成本高而且和个人足迹记录关系不大。不强制联网。如果只是记录足迹数据完全可以保存在本地上线版本可以用接口做同步但核心功能不应该依赖后端。边界清楚了项目规模就控制住了。这也是踩坑之后才想明白的很多工具不是功能不够而是功能太散。3. 技术实现思路地图库、数据格式和存储方案3.1 地图展示层怎么选地图展示是核心但选择并不复杂。常见方案有方案优点注意点Leaflet 类库轻量、开源、可自定义默认底图通常是国际通用瓦片国内使用时要确认可访问性和瓦片策略国内地图 JS API加载快地名和道路数据贴近本地习惯一般需要申请 Key可能有配额限制ECharts 地图适合做统计展示不依赖复杂交互如果要做地图缩放和 marker 点击需要额外处理自绘 SVG 地图完全可控不依赖第三方数据维护成本高适合固定版本的地铁图如果你做的是学习项目我建议优先选轻量开源地图库配一个公开瓦片源。好处是不用申请 Key开发时上手快不足是国内各地图服务的坐标系和瓦片风格可能不一致需要自己调整站点坐标。如果你做的是面向大众的上线版本可以考虑国内地图服务商。步骤一般是注册账号、创建应用、申请 Key然后把 Key 配置到前端。要注意的是Key 一旦打包到前端实际上就是公开的所以要通过域名白名单、请求频率限制等手段降低风险。3.2 站点数据怎么组织无论选什么地图核心数据都是“站点清单”。我建议用 JSON 或 GeoJSON 维护一份静态数据文件至少包含以下字段站点ID全站唯一用拼音或数字都可以但不要直接用名称作为 ID因为站名可能变更。站点名称展示用。所属线路一个换乘站可能属于多条线路要用数组。坐标经纬度。线路颜色方便按线路统计图表展示。示例数据结构{ id: xizhimen, name: 西直门, lines: [2号线, 4号线, 13号线], lat: 39.938, lng: 116.353 }这只是一个示例坐标数据需要自己从公开渠道整理并校正。对于换乘站不要把它拆成多个站点而要让一个站点持有多个线路标记。否则统计“2号线是否包含西直门”时会出现重复计数。线路数据也可以单独维护一份{ line: 2号线, color: #006098, stations: [西直门, 积水潭, 鼓楼大街, ...] }有了这个数据前端可以按线路顺序绘制连线也可以按线路统计已乘坐数量。3.3 存储与状态管理最小版本的数据量并不大。一个城市的地铁站大约几百站每站一条记录完全可以在浏览器本地保存。最简单的做法是把“已乘坐站点ID集合”存到 localStorage 里key 可以叫 visitedStationsvalue 是 JSON 数组。这样做的优势很明显不需要服务器纯静态页面就能跑。数据读写快刷新页面不丢。上线部署简单任意静态托管都能用。缺点也很明显换浏览器或者清除浏览器数据后记录会丢失。同一站点在多端之间无法同步。如果上线版本希望让用户在不同设备看到同一份足迹就需要引入后端和数据库。可以选择一个轻量后端服务提供两个接口一个读取用户足迹一个保存用户足迹。用户身份可以用简单的账号密码或第三方登录。但我的建议是先把 localStorage 版本做好。个人项目最怕一上来就写注册、登录、接口权限这些内容会把核心的“足迹地图”淹没。4. 实操流程从数据整理到跑通第一个足迹4.1 第一步准备站点基础数据地图功能开发之前先整理站点数据。这个环节看起来简单实际最费时间。首先要确定数据来源。可以从公开的地铁线路图、百科词条、城市数据开放平台获取线路和站点信息。如果使用他人整理好的数据集一定要看许可证确认能否用于开源项目。整理数据时要注意几件事线路名要统一。是写“地铁2号线”还是“2号线”建议代码里只保留规范化简称。站点坐标要统一坐标系。国内很多地图服务使用 GCJ-02 坐标而部分开源瓦片使用 WGS-84 坐标。如果站点坐标和底图坐标不匹配标记会整体偏移。换乘站命名要一致。同一个换乘站在不同线路中的名称可能相同但也不排除历史叫法不一样。整理时最好以官方最新站名为准同时保留别名或历史名。建议把数据写成 JSON 文件放在项目独立目录下例如 data/stations.json 和 data/lines.json。这样以后更新站点不需要改前端逻辑。4.2 第二步构建页面和地图初始化页面结构可以很简单一个顶部统计栏一个主体地图区域再加一个侧边站点列表。地图初始化时先读取 JSON 数据遍历站点数组把每个站点作为一个可点击的 marker 添加到地图上。marker 的坐标使用站点经纬度点击事件里更新标记状态。初始化逻辑的伪代码类似const stations await fetchStations(); const visitedSet loadVisitedStations(); stations.forEach((station) { addMarker({ position: [station.lat, station.lng], id: station.id, onClick: () toggleStation(station.id) }); });这一步要注意 marker 的数量。北京地铁站有几百个全部渲染成普通 marker 可能会让页面卡顿。如果性能有问题可以改用 Canvas 图层或合并显示方案。低配机器上优先保证交互流畅不要堆叠太多高消耗组件。4.3 第三步实现打卡核心交互打卡逻辑的核心是维护一个 visitedSet。每次点击站点就判断该站点 ID 是否在集合中如果不在加入如果已经存在移除。为什么用 ID 而不是站名因为站点可能改名改名后只要 ID 不变历史打卡数据还能对应上。展示层用 name存储层用 id两者分离。点击后的视觉反馈至少要包含两种情况未乘坐时灰色或半透明 marker。已乘坐时高亮颜色并显示一个小人的足迹图标或特殊样式。同时更新统计区域。最简单的公式是已乘坐站点数 visitedSet.size总站点数 stations.length覆盖率 (已乘坐站点数 / 总站点数) * 100%这个统计看起来简单但要注意换乘站是否重复计数。如果站点数据里已经把换乘站合并为一条记录那么总数就是实际物理站点的数量如果按线路拆开则统计维度会变成“线路站点数”需要区分清楚。4.4 第四步统计和足迹渲染单站点亮完成之后可以继续做按线路统计。遍历 line 数据对每条线路的站点列表逐站判断该站点是否在 visitedSet 中。如果在则该线路的已完成数加一。最后算出每条线路的覆盖率。这一步比较关键的数据结构是“线路到站点”的映射关系。如果你的站点数据是多线归属那么通过站点查线路容易通过线路查站点则需要单独维护 lines.json。足迹渲染可以分两种点状足迹只点亮已乘坐站点。线状足迹把已乘坐且相邻的站点用线段连接起来看起来更像“走过的路”。线状足迹看起来很好看但需要线路拓扑数据而且必须判断两个站点是否在同一线路上相邻。如果中间有站点没坐过线要怎么断开这些都是需要细化的问题。我建议先做点状足迹线状足迹作为后续迭代。4.5 第五步部署上线开发完成后构建产物是纯静态文件。部署到任意 Web 服务器即可。以下是示例命令实际以你的项目脚本为准npm install npm run build然后把构建产物放到服务器或静态托管平台。需要检查三件事资源路径是否正确。如果部署在子目录比如 https://example.com/subdir/那么 JS、CSS、图片资源路径必须使用相对路径或正确配置 base 路径。浏览器控制台是否报错。部署后第一时间打开控制台看网络请求404 通常说明资源路径配置不对。数据请求是否跨域。如果站点数据是独立 JSON 文件并且域名和页面一致一般没问题如果从 CDN 或后端 API 加载需要确认跨域配置。上线后先用手机浏览器测一遍。重点看两件事地图拖动是否流畅点击站点是否准确。很多桌面端没问题的交互在触屏上可能反应迟钝。5. 开源之前必须处理的事5.1 开源许可证选什么代码写完了决定开源先要选一个许可证。常见选择是 MIT、Apache-2.0 和 GPL-3.0。许可证允许商用需要保留版权声明修改后是否必须开源MIT是是否Apache-2.0是是否GPL-3.0是是是如果你希望别人能自由使用和修改不强制要求再次开源可以选 MIT 或 Apache-2.0。如果你希望后续衍生项目也必须开源可以选 GPL-3.0。我自己的经验是个人工具选 MIT 最省事但如果你想保护更复杂的项目Apache-2.0 会让授权边界更清楚。许可证一旦选定最好不要频繁更换因为会影响到使用者。5.2 README 和示例数据开源仓库最重要的不是代码而是 README。一个合格的 README 应该包含项目简介这个工具是干什么的。在线体验地址如果有的话放在最显眼的位置。功能截图一到两张即可能直观展示足迹地图效果。本地运行方式需要什么环境、安装什么依赖、启动命令是什么。数据格式说明站点 JSON 和线路 JSON 的字段说明。如何贡献新线路开通、站点改名、坐标纠偏别人应该提交什么文件。示例数据也很重要。不要直接把开发者本人的完整打卡数据作为唯一数据提交到仓库因为那可能包含个人隐私。更合适的做法是提供一份脱敏的 demo 数据比如只标记几站作为演示让大家知道效果长什么样。5.3 数据和维护思路开源不是为了把代码丢上去就不管。实际维护中最常见的问题是数据更新。地铁线路会变站名会改新线路会开通。如果站点数据硬编码在前端逻辑里别人想帮你更新就很麻烦。更好的做法是把站点数据抽成独立 JSON 文件贡献者只改数据不碰代码。这样既降低了参与门槛也减少了代码冲突。接受 issue 时我会建议贡献者先说明数据的来源最好附上官方公告或地图截图。坐标数据不能靠肉眼猜必须经过验证。5.4 隐私和信息安全地图类工具容易触碰隐私问题。开源代码里不要内置真实用户的打卡数据和账号信息。部署上线版本时如果支持用户登录后端要注意接口的权限控制如果只是本地记录则不需要担心服务端数据泄露但仍然要在 README 里说明数据保存在哪里。不要把地图 Key 提交到代码仓库。前端 Key 虽然无法完全保密但至少不要把它写进公开仓库后放任不管。正确做法是作为环境变量配置部署时再注入。6. 实战中容易踩的坑和排查顺序6.1 坐标偏了站点显示不在轨道上这是我第一次跑通地图时最容易遇到的问题。肉眼看到 marker 离站点名称有一段距离或者整片站点整体偏移。优先检查坐标系。国内厂商的地图底图坐标通常经过偏移转换而部分开源瓦片或自己下载的地理数据可能使用国际通用坐标。两者混用就会导致偏移。排查顺序先看所有 marker 是否朝同一个方向偏移。如果整体偏移大概率是坐标系不匹配。如果是个别站点偏移优先检查那几站的基础数据是否写错。如果只有某个线路偏移检查线路数据和站点数据的关联是否弄反。6.2 站点名称变化历史记录突然对不上北京地铁的站名会调整比如某些站点因为附近地名变化或换乘线路开通而改名。如果持久化数据里只存了站名字符串改名后旧记录就失效了。这也是我一直强调用站点 ID 做持久化 key 的原因。展示层可以随时换成新站名但历史记录关联的是稳定 ID。如果之前已经用站名存了数据建议写一个 local 数据迁移函数把旧站名映射到新 ID。6.3 部署后页面白屏或地图加载失败本地开发正常部署到服务器后白屏这是前端部署最常见的坑之一。排查顺序打开浏览器控制台查看报错信息。看 Network 面板检查 JS、CSS、JSON 文件是否 404。如果你的页面部署在子路径下检查资源路径是否缺了前缀。如果是地图瓦片加载失败检查网络环境和地图服务的可用性。如果是接口跨域问题看是否有跨域限制。很多白屏问题不是代码逻辑错而是路径和部署环境不对。6.4 数据文件维护不规范数据文件里的字段名如果前后不一致比如有的站点写 lat有的写 latitude渲染时会直接报错或显示 undefined。建议给站点数据加一个最基本的校验。可以在构建时写一个简单脚本检查每个站点是否包含 id、name、lines、lat、lng 字段并检查 ID 是否重复。没有校验的情况下地图能正常显示但数据有问题时会很难定位。6.5 性能问题的排查所有站点 marker 一次性渲染低端手机可能卡顿。如果遇到这样的问题可以从这几个方向排查减少单个 marker 的资源消耗比如使用更简单的图标。使用 Canvas 代替大量 DOM 或 SVG marker。在地图缩放级别较低时只显示站点名称不渲染图标。开启地图的聚合效果但在站点数量不太多时聚合也可能增加额外计算。判断标准很简单打开页面后拖动地图如果帧率明显下降或点击延迟超过一秒就需要优化。如果只是偶尔卡一下可以暂时接受。项目做到最后我的最大感受是地图本身不复杂真正花时间的是站点数据维护和边界处理。如果你想练手地图可视化不妨也试着做一个你所在城市的足迹地图。先把单机打卡跑通再考虑统计、分享、开源。这条路一开始看起来不长做起来才会发现每一层都有值得细抠的地方。