GeoJSON 文件本身并不难懂,真正恼人的是当你改完坐标或属性,却没办法在几秒内直观地确认它到底长什么样。Simon Willison 这类长期记录 AI 编程实践的开发者,带火了一种很省事的做法:不找在线工具,不手写完整页面,而是把需求直接丢给 Claude Code,让它在几分钟内生成一个可本地运行的 GeoJSON Map Viewer。听起来像一句玩笑,但这个思路真正改变的不是“会不会写地图代码”,而是调试地理数据时的反馈回路。
我更愿意把这类实践拆开来看:一个 AI 辅助生成的小工具,背后其实藏着三个关键判断——什么时候值得为一次性需求写代码、生成出来的代码怎么验证、以及一段代码如何沉淀成可复用的本地工作流。这篇文章就从这三个问题展开。
1. 这个工具解决的问题不是“看地图”,而是地理数据的调试节奏
1.1 GeoJSON 是调试地理数据时最常接手的“中继格式”
GeoJSON 本质上就是一个 JSON 对象,核心结构是 FeatureCollection 包着一堆 Feature,每个 Feature 有 geometry 和 properties。geometry 用来描述点、线、面,properties 用来挂业务属性。
{ "type": "FeatureCollection", "features": [ { "type": "Feature", "properties": { "name": "示例点", "level": 3 }, "geometry": { "type": "Point", "coordinates": [116.4, 39.9] } } ] }这种格式之所以流行,是因为它同时具备两个优点:第一,它是纯文本,任何语言都能解析、生成、比对;第二,它的结构足够统一,从 QGIS 导出来是它,从各种坐标转换工具里拿到的也是它,地图 SDK 也都能消费它。
但也正因为它是“中间格式”,很多问题只有在可视化之后才会暴露。比如某个多边形的环方向反了,坐标对调了经纬度,或者 properties 里塞了 null 导致样式分支崩掉。只看文本是很难看出这些问题的。一次快速的 map viewer 就是用来解决“到底对不对”这个问题的。
1.2 在线查看器和完整 GIS 工具覆盖不到的场景
遇到 GeoJSON,很多人第一反应是打开在线查看器。这个选择在文件很小、内容不敏感时是合理的。但实际工程里经常碰到三种情况:
- 文件来自内部业务系统,坐标和属性都算敏感信息,不适合上传到第三方在线服务。
- 文件可能有几十 MB,在线工具要么限制大小,要么处理起来卡顿。
- 你需要的不只是“看”,还要临时检查某个字段、过滤某些要素、对比两个文件的差异。
这时候打开 QGIS 当然也行,但对一个只需要验证十分钟的需求来说,启动一个完整 GIS 软件的成本确实偏高。你需要的其实是一个足够轻、能在本地跑、还能按自己需求加功能的小页面。
这正好是 AI 辅助编程最合适的应用区间:需求路径清晰、技术选型简单、代码量不大,但写起来又有点琐碎。
1.3 一个值得记住的判断
这个 GeoJSON Map Viewer 真正有价值的地方,不是它渲染的地图有多漂亮,而是它把“改数据→看结果”的周期从“拷贝到网站上传→等响应→人工找问题”压缩到了十几秒。工具是小的,反馈回路是快的,这才是这类实践的长期意义。
注意:让 AI 生成工具之前,先想清楚你要解决的是“偶尔看一眼”,还是“长期调试”。这决定了后面所有技术取舍。
2. 用 AI 助手生成工具,关键不在“生成”,而在约束和验证
2.1 第一次描述需求就该说清楚五件事
很多人拿到 Claude Code 之后,第一句只问“帮我写一个 GeoJSON 查看器”。模型确实能写出代码,但大概率会给你一个通用模板。通用模板没有错,只是经常缺了你真正关心的细节。
我更建议在第一次交互时就把下面五件事说清楚:
- 输入方式:是拖拽文件、文件选择框,还是命令行参数。
- 渲染方案:选 Leaflet、MapLibre GL,还是纯 Canvas。
- 交互动作:点击要素后要不要展示属性,要不要支持样式切换到 fill/point。
- 错误处理:非法 JSON 怎么提示,空文件怎么处理,坐标系不对要不要告警。
- 依赖约束:用 CDN 还是本地构建,要不要兼容离线环境。
一个可参考的指令是这样:
在 geojson-viewer 目录下创建一个纯前端单页工具: 1. 允许用户拖入 .geojson 文件,也支持文件选择框加载; 2. 使用 Leaflet 渲染地图并自动 fitBounds; 3. 点击某个要素时,在右侧展示它的 properties; 4. 如果文件不是合法 GeoJSON,在页面上给出明确错误提示; 5. 使用 CDN 引入依赖,不引入构建工具,双击 html 就能尽量跑起来。关键在于:给模型足够多的边界条件,它生成的代码才不是“看起来像”,而是“能替你省下一步排查时间”。
2.2 地图渲染方案怎么选
GeoJSON Map Viewer 的渲染选型,直接影响生成代码的复杂度和后续维护成本。
| 方案 | 定位 | 适合场景 | 主要限制 |
|---|---|---|---|
| Leaflet | 轻量、上手快 | 快速调试、展示点线面 | 样式能力相对简单 |
| MapLibre GL | WebGL 矢量渲染 | 自定义样式、复杂交互 | 学习曲线更高,包体积更大 |
| OpenLayers | 功能全面 | 复杂坐标操作、多源数据 | API 偏厚重 |
| 自绘 Canvas/SVG | 不引外部依赖 | 只想看几何轮廓 | 投影、交互、底图都要自己做 |
从“让 AI 几分钟生成一个调试工具”这个目标看,Leaflet 是多数情况下的默认选项。原因很简单:API 清晰,出错概率低,CDN 可以直接引用,模型生成出来的代码也更容易验证。
如果你的使用场景涉及复杂自定义样式、3D 地图或超高密度数据,那再考虑 MapLibre GL 也不迟。先跑通,再升级,这是更稳的路径。
2.3 模型、客户端和账号之间的兼容性,是第一个隐形坑
这次公开实践里提到一个细节:代码生成时需要选模型,但模型不是你想用就能用。类似海外社区里出现过the 'gpt-5.6-sol' model is not supported when using codex with a chatgpt account这样的提示,先不管 GPT-5.6-Sol 这个代号是正式版本还是某次演示里的内部叫法,它说明了一个更普遍的现实:模型可用范围由账号类型、客户端和订阅状态共同决定。
放到 Claude Code 里也一样。你能调用什么模型、能不能用高级模型,取决于你的 Claude 订阅账号、组织策略和客户端版本。不少人遇到Your organization has disabled Claude subscription access for Claude Code,这不是命令敲错了,而是组织层面把订阅入口关掉了。
所以在开始之前,建议先确认三件事:
- 你的 Claude Code 客户端是否最新版本;
- 当前登录的账号是否有权限访问你要用的模型;
- 如果通过 Codex 等其他客户端操作,同一个模型可能不在可用列表里。
用一句工程上的话概括:模型只是一个执行单元,客户端和账号才是真正的调度入口。排查问题的时候,不要一开始就怀疑你的提示词,先看看模型是不是真的在当前环境里开放。
3. 从零复现一个最小可用的 GeoJSON Map Viewer
3.1 准备环境和样例数据
Claude Code 的安装方式以官方文档为准,常见做法是通过 npm 全局安装后,在项目目录里运行。
npm install -g @anthropic-ai/claude-code claude --version如果你刚接触这类终端工具,可以把它理解成一个能读文件、能执行命令、能写代码的“结对程序员”。它需要的是一个项目目录,而不是一条空命令。
先建目录,再放一个测试文件:
mkdir geojson-viewer cd geojson-viewer然后把前面那一段示例 GeoJSON 存成sample.geojson,方便后续验证。多准备两个极端样例更好,比如空 FeatureCollection、没有 properties 的 Feature、坐标系异常的数据,这些在验证阶段会派上大用场。
建议:不要一上来就追求功能齐全,先用一个最小文件跑通整条链路,再逐步加需求。
3.2 让 Claude Code 干活的指令怎么写
在项目目录里启动会话后,你可以直接把需求写成自然语言指令。要注意,Claude Code 这类工具通常能读取目录里的现有文件,所以你可以先说明目录结构,再提具体目标。
当前目录下有一个 sample.geojson,请帮我创建一个 GeoJSON Map Viewer。 要求: - 单 HTML 文件,使用 Leaflet CDN; - 页面加载后默认显示世界地图; - 文件选择框 + 拖拽两种方式加载; - 加载成功之后自动 fitBounds; - 点击一个要素时,在页面下方列出它的 type、properties 和坐标层级; - 文件不是合法 GeoJSON 时,显示错误信息; - 不要引入 Vue 或 React,直接原生 JavaScript。第一次生成的代码可能不完全符合预期,这很正常。接下来要做的不是推倒重来,而是让模型读一下输出文件、理解结构后,再针对具体问题修。
3.3 核心页面结构(Leaflet 版)
下面是一个最小可运行的结构,用 CDN 引入 Leaflet,页面支持文件选择和拖拽读取 GeoJSON。如果你让 Claude Code 生成,最终代码结构大体会类似。
<!doctype html> <html lang="zh-CN"> <head> <meta charset="utf-8" /> <title>GeoJSON Map Viewer(本地调试版)</title> <link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" /> <script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script> <style> body { margin: 0; font-family: system-ui, sans-serif; } #map { height: 70vh; background: #eee; } #info { padding: 12px; font-size: 13px; white-space: pre-wrap; } </style> </head> <body> <div id="map"></div> <pre id="info">把 .geojson 文件拖到页面里查看</pre> <script> const map = L.map('map').setView([35, 110], 4); L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', { attribution: '© OpenStreetMap contributors' }).addTo(map); let currentLayer = null; let featureMeta = []; function handleGeoJSON(geojson) { if (currentLayer) map.removeLayer(currentLayer); currentLayer = L.geoJSON(geojson, { onEachFeature(feature, layer) { featureMeta.push({ id: layer._leaflet_id, properties: feature.properties }); layer.on('click', () => { document.getElementById('info').textContent = JSON.stringify(feature.properties, null, 2); }); } }).addTo(map); if (currentLayer.getLayers().length > 0) { map.fitBounds(currentLayer.getBounds()); } } document.addEventListener('dragover', e => e.preventDefault()); document.addEventListener('drop', e => { e.preventDefault(); const file = e.dataTransfer.files[0]; if (!file) return; const reader = new FileReader(); reader.onload = () => { try { const geojson = JSON.parse(reader.result); handleGeoJSON(geojson); } catch (err) { document.getElementById('info').textContent = '解析失败: ' + err.message; } }; reader.readAsText(file); }); </script> </body> </html>这个版本的代码做了两件重要的事:一是加载后通过fitBounds自动把视野对准数据范围;二是点击要素时展示 properties。这正是调试 GeoJSON 时最常用的两个动作。
3.4 从“生成出来”到“可以信任”的验证清单
AI 生成的代码不是不能信任,而是需要验证路线。推荐按以下顺序检查:
- 页面能否用本地服务启动,而不是直接双击 html 文件。因为浏览器在
file://协议下对本地文件读取有限制,建议先起一个本地静态服务。 - 用合法的 GeoJSON 文件加载,确认地图出现要素并自动缩放。
- 用一个损坏的 JSON 文件加载,确认能出现错误提示而不是白屏。
- 点击要素,确认 properties 能展示出来。
- 用空 FeatureCollection 加载一次,确认没有报错或死循环。
在项目目录里,最简单的本地服务命令是:
python3 -m http.server 8000然后打开http://localhost:8000/访问页面。如果你用了拖拽读取,其实很多时候直接拖文件到页面即可,文件不会经由服务器上传,仍然是本地处理。但静态服务的做法更接近日常前端调试环境,也更容易看到浏览器控制台日志。
4. 参数、边界与真实调试里的常见坑
4.1 输入侧最容易被忽略的四个问题
GeoJSON 渲染失败,很多情况下不是代码问题,而是输入数据本身有“隐藏特征”。常见的坑至少有以下四个。
第一,坐标系。GeoJSON 规范默认使用 WGS84,也就是经纬度顺序是[经度, 纬度]。如果你手里的数据是从某些本地规划系统转出来的,坐标可能是投影坐标或[纬度, 经度]顺序。渲染出来的要素跑到海里,多半是这个问题。
第二,顶层类型。有的数据是FeatureCollection,有的直接是单个Feature,还有的是GeometryCollection。好的 viewer 应该同时处理这几种情况。如果代码只处理 FeatureCollection,遇到单 Feature 就会把整个文件当成无效数据。
第三,properties 的缺失和空值。GeoJSON 允许 properties 为 null,点击要素时如果直接展示feature.properties.name,很可能报错。生成代码时要让模型明确处理 null 和 undefined。
第四,大文件和超长坐标。Leaflet 在几千个点的时候表现尚可,但如果一份 GeoJSON 有几十万个点,浏览器直接渲染就会卡顿。这时候需要预简化、聚合或切片。换句话说,小工具会有性能边界。
4.2 渲染侧的坐标顺序和边界行为
即便输入合法,渲染侧仍然有几个常见坑。
一个坑是fitBounds在空图层上调用,会直接报错或者没反应。所以正确顺序是先判断getLayers().length > 0,再决定要不要自动缩放。
另一个坑是 Leaflet 的坐标翻转。虽然 Leaflet 内部支持 GeoJSON 的[经度, 纬度],但当你手动从经纬度创建 marker 或者直接使用L.marker([lat, lng])时,顺序是反过来的。AI 生成的代码经常会在这两种 API 之间混用,一旦混淆,marker 就会跑到反方向的对跖点附近。
解决办法是让生成的工具统一在一个入口函数里处理坐标,不要在业务代码里到处写经纬度。模型生成的第一版通常不会主动抽象这层,所以需要你手动补充约束。
4.3 安全与隐私边界
这个 viewer 虽然是在本地跑,但仍然要划清隐私边界。
- 如果文件是内部坐标数据或业务属性,即便只是打开本地页面,也要确认没有把文件写到某个在线服务。
- 代码里如果引用了在线底图,底图请求会携带当前视野的经纬度。对敏感项目来说,这本身也是一个信息暴露点。最好的方案是使用内部离线底图或者只显示几何轮廓。
- 不要把含敏感信息的 GeoJSON 直接粘贴到任何网页对话框或第三方模型中。这部分边界要靠用户判断,不能依赖工具。
一句话:工具在本地,不代表数据不出门。
4.4 什么时候不值得让 AI 自己写
也不是所有 GeoJSON 查看需求都应该写代码。如果只是偶尔看一个几 KB 的小文件、数据完全公开,在线查看器已经够用。只有当你需要频繁处理内部数据、需要加自定义检查逻辑、或者想让整个团队都能在本地复现同样流程时,把 viewer 沉淀为本地工具才划算。
判断标准很简单:如果这个需求未来三个月会出现三次以上,就值得让它变成一个可复用工具;如果只是一次性确认,直接找现成方案更省时间。
5. 把一次演示变成自己的调试工作流:五个可复用步骤
5.1 五步落地法
这次“AI 辅助做一个小工具”的过程,真正沉淀下来的是下面五步。以后遇到其他数据处理需求,也可以按这个框架走。
第一步,定义输入和输出。输入是 GeoJSON 文件,输出是浏览器渲染结果 + 属性信息页。先讲清楚这条链路,后面建模才不会偏。
第二步,明确技术约束。Leaflet、CDN、无构建工具,这些约束越早说越好,能省掉很多返工。
第三步,生成并单次跑通。用最小样例数据验证流程没有断。这里的目标不是功能多,而是能跑。
第四步,用边界数据验证。准备空文件、损坏文件、null properties、大文件,分别跑一遍。边界测试里暴露的问题,通常才是真实数据里会遇到的问题。
第五步,沉淀到本地目录。把用过一次的 prompt 记录在 README 里,把样例数据放在examples/目录下。下次再做类似 viewer,就不需要从零开始。
5.2 出问题时的排查链路
如果你的 GeoJSON Map Viewer 生成出来但表现不对,不要急着让模型一遍遍重写。按下面的顺序排查更高效:
- 看现象:是白屏、加载没反应、报错还是渲染出来位置不对。
- 看输入文件:打开 JSON,确认结构合法、是 FeatureCollection 还是单 Feature、坐标数量级是否正确。
- 看浏览器控制台:语法错误、跨域限制、CDN 加载失败都会在这里留下痕迹。
- 看依赖版本:Leaflet 的 CDN 版本是否写死、是否被拦截、有没有引入两次。
- 看触发逻辑:拖拽事件是否阻止了默认行为、FileReader 是否真的读到了文本。
- 最后看模型生成时的兼容性:确认你当前用的客户端和账号确实支持所选模型。
这个顺序的核心是“先判断是哪一层坏了,再决定是改数据、改代码还是换环境”。如果一上来就反复重写整个 html 文件,很可能把问题掩盖下去,而不是解决掉。
5.3 比工具更重要的,是保留判断力
AI 生成工具的体验很像是雇了一个反应很快、但偶尔会“自信地说错答案”的实习生。它可以帮你把骨架搭好,但最后确认坐标方向对不对、属性字段全不全、极端文件下会不会崩,这些边界判断仍然要由你来做。
项目标题里出现的 GPT-5.6-Sol 或 Claude Code,本质上都只是这段工作流里的执行单元。真正决定工具质量的,是你定义的输入边界、你补充的验证样例、以及你在发现错误后能不能准确描述问题。这些能力不会随着模型更新而自动获得,反而会在你反复使用这类工作流时慢慢积累下来。
我建议你从今天开始,就用一个真实项目里最常碰到的 GeoJSON 文件做实验:把它拖进本地生成的 viewer,看看多长时间能找到问题。当这个时间从半小时缩短到几分钟时,你大概就能理解 Simon Willison 这类开发者为什么愿意花时间让 AI 帮忙做这些看起来并不“高级”的小工具了。因为它们解决的不是技术难度,而是重复劳动里最容易被忽略的那部分时间成本。