中国 2,200 年,被压进一个网页,还能拖着看
18 个朝代、164 张疆域快照,纯前端零依赖。更狠的是它让 AI 不用知道经纬度,也能定位一句诗里的地点——拆解「江山时序」的三个设计。
读古文时,脑子里那张地图是空的
「即从巴峡穿巫峡,便下襄阳向洛阳。」杜甫写这句是公元 763 年,安史之乱刚平,他在梓州听到官军收复失地的消息,想立刻顺江东下回中原。
这句诗你大概背过。但巴峡、巫峡、襄阳、洛阳分别在今天哪里,中间隔多远,走水路要几天——多半是模糊的。
这不是记忆力的问题。中国历史文本里塞满了地名,而其中一大半今天已经不存在了:州、郡、道、路、府,每一级都随朝代改。你记住的是名字,脑子里缺的是坐标。
「江山时序」解决的就是这件事。它目前 3,357 Star、398 Fork,GPL-3.0 协议,纯 JavaScript——没有后端,没有构建步骤。数据是我从 GitHub API 和站点自己的数据文件里核的,不是转述别人的介绍。
它只做三件事,但三件事咬得很紧
打开首页,最先看到的是底部那条时间轴:战国、秦、西汉、东汉、三国、西晋、东晋十六国、南北朝、隋、唐、五代、北宋、南宋、元、明、清、民国、现代,18 个朝代一字排开,下面是年份刻度,从公元前 403 年一直排到今天。
拖一下滑块,地图上的色块跟着变大变小。到这儿还不算稀奇——静态的疆域 GIF 早就有了。它多做的是两件事。
点地溯源
点地图上任何一座城,弹出一张沿革卡:从最早见于记载的名字,到历代所属的郡、州、路、府,再到今天的省市,中间夹着发生在这块地上的大事。
长安那张卡里,从西周一路到唐,每一朝的名字变更和建都时间都在。它把「地名沿革」做成了一条能点开的时间线,而不是一张干巴巴的对照表。
点人溯事
搜一个人的名字,它把这个人一生走过的地点按年份连成路线,同时自动切到对应的朝代底图。
苏轼这条线,教科书上一句「一贬再贬」就带过了。画在地图上才发现:从湖州到儋州横跨整个中国,而走这条路时他已经六十岁。
作者在自述里说,这三个功能不是一次想好的,是一条接一条长出来的:先把时间这根横轴做出来,就想能不能从一座城出发、走地理这根纵轴;横纵都有了,人自然就走进来了。
有了横纵两条坐标轴,天与地都有了,那作为历史主角的人,是不是也能自然而然地走进这张地图?
—— 作者自述
最漂亮的一个决定:让 AI 不知道经纬度
这是我认为整个项目最值得抄的设计。
现在的通行做法是给大模型一个地图 API,让它自己查坐标。问题是模型会编经纬度——给一个看着像那么回事的坐标,然后在撒哈拉边上插面旗。
江山时序反过来:它不要坐标,只要两个语义参数——年份和地名。
于是有了这套「零坐标 URL 协议」,三种精度:
- 单点定位:
?year=819&place=蓝关—— 跳到唐元和十四年,在秦岭上标出韩愈「雪拥蓝关马不前」那座关 - 同年多点:
?year=763&place=剑外,巴峡,巫峡,襄阳,洛阳—— 在 763 年的唐图上连出杜甫那条回乡路线 - 跨年时空:
?year=1079,1080,1094,1097&place=湖州,黄州,惠州,儋州—— 年份与地点一一配对,苏轼四次贬谪按年排开
上面这张就是第二条链接直接渲染出来的结果。没有任何人工标注,只给了年份和五个地名,它自己算出了 991 公里的全程和各站位置。
这背后是一整套给 AI 用的接口:
| 入口 | 作用 |
|---|---|
llms.txt | 机器可读的规范,告诉模型能传什么参数 |
| 本地 MCP Server | 零 npm 依赖,3 个工具:查地点、查人物、全文搜索,离线响应 5ms 内 |
| Agent Skill | 带一个 Python CLI,一条命令直接截出地图 PNG |
| atlasAPI / WebMCP | 浏览器里也能被 Agent 直接驱动 |
那个 MCP Server 走 stdio,接进 Claude Desktop 或 Cursor 只要在配置里加一行路径。没有 npm install,没有依赖树,这点在 MCP 生态里相当少见。
这意味着什么:让 AI 写一篇讲杜甫生平的稿子,它可以自己调这个工具,把每个地点标出来、截成图,不用人肉去截图拼版。
这段不是推测。我把它的 CLI 装上跑了一遍:--person 苏轼 一条命令,8 秒后拿到一张 2880×1800 的图——地图上 12 个节点从眉州一路连到常州,右侧是完整的生平纪事,每个节点都挂着年份、当时的州府名、今天的城市名,还有对应的诗句。
再加一个 --inspect,同一份数据还能拿到 JSON:dynasty、polity、modernProvince、nearestCity 全在字段里,连「成都府路文献名邦,三苏故里」这种考据注都带着。这意味着做历史视频的人,分镜脚本和地图素材可以是同一次调用出来的。
技术底子:一个不装框架的地图
把前端翻一遍,有几个数字值得说。
第一,没有地图库。没有 Leaflet、没有 Mapbox、没有 D3,也没有 Vue 或 React。整个项目就是几个 JS 文件加一份 CSS。README 写得很直白:克隆下来,python3 -m http.server 8000,打开就能用。
第二,省界是自己压的。china_geo_data.js 那 85KB 里,装着全国省级边界的完整多边形——用的是 VLQ 增量编码,跟 Mapbox Vector Tile 压缩坐标是同一套思路:把经纬度差分成变长整数,再用自定义 base64 变体编码。文件头一行写着「Lossless VLQ Delta-Encoded Rings」,无损。
第三,数据全内联。window.DYNASTY_DATA 和 window.ATLAS_LORE_DATA 是两个巨大的 JS 字面量。把里面的条目数出来是这样:
| 内容 | 数量 |
|---|---|
| 朝代阶段 | 18 朝 · 164 张疆域快照 |
| 帝王纪年 | 351 条 |
| 地点史事 | 687 条 |
| 人物档案 | 124 位 |
| 政权档案 | 110 个 |
| 古今地名 | 186 条 · 87 座城 |
| 名关雄隘 | 20 座 |
| 历史行迹 | 7 条 |
这些条目从哪来,值得单独说一句。README 的 Credits 列了四份底本:谭其骧主编《中国历史地图集》、哈佛燕京学社与复旦的 CHGIS V6、Robert M. Hartwell 的中国历史 GIS 数据集,以及 aourednik 的 historical-basemaps;今天的省界用的是 DataV GeoAtlas 公开行政区划。
CONTRIBUTING 里还有条硬规矩:要改边界,必须在 PR 里写明参考的是哪本图集的哪一册哪一幅。这不是随手画的色块。
但这里埋着一处授权陷阱。 郡级边界和战国疆域派生自 CHGIS V6 与 Hartwell 数据集,这两份原始数据限定学术、非商业用途,作者明确写了它们没有随 GPL-3.0 一起再许可。整站是 GPL,但那部分几何不是。看、引用、写文章都没问题,拿去做商业产品之前,得先把这条读明白。
取舍很清楚:没有 API,所以搜一搜爬得到、离线能用、部署成本接近零;代价是首屏要下 2MB 出头的数据,第一次打开不算快。
还有个细节挺有意思:右上角那个「雅乐」按钮,点开会放古琴曲——《流水》《平沙落雁》《阳关三叠》《醉渔唱晚》《酒狂》,五首,数据里就带着音频地址。地图配古琴,这不太像工程师会做的事。
短板也很明显
先说最要紧的一句:它的历史疆域与政区是示意性近似,不是精确测绘边界。这话不是加的,是站点自己写在页面右下角的免责声明。看趋势、找位置没问题,拿去做严肃的边界论证就别了。
数据校勘是靠人力一点点磨的。作者自述里提了两个例子:有一次放大地图才发现,辽国上京被画在了辽国疆域外面;还有一次,横跨欧亚的蒙古帝国在罗布泊那儿莫名破了个洞。这类错误在历史地图里太常见,他的处理方式是翻《资治通鉴》和各朝正史一条条对。
另外它的 GeoJSON 导出只覆盖到 1948 年,之后的部分和现代省界不在导出范围内。想拿去做二次开发,这点得先知道。
还有个挺现实的细节。作者把项目发小红书、发知乎都被限流,投给几个中文 GitHub 推荐社群也没什么动静。最后是 X 上有人转发,几天涨了一千多 star。火了之后,倒有不少人搬到小红书和公众号上,拿到了不错的流量。
这段读起来有点不是滋味,但它确实说明一件事:好项目不等于好传播。
三种用法,和一个彩蛋
想逛的,直接开网页,不用注册:deanchen.me/jiangshan-map,手机端也能用。
想本地跑的,把仓库克隆下来,进目录执行:
python3 -m http.server 8000
浏览器打开 localhost:8000。没别的步骤,不需要 node,不需要装包。
想喂给 AI 的,在 MCP 客户端里加一段配置,把 command 指到 node、args 填仓库里 scripts/mcp_server.js 的绝对路径就行。或者直接用仓库自带的 Agent Skill,里面有 CLI:--year 819 --place 蓝关,跑完直接给你一张图。
另外藏了个彩蛋:古地名挑战。随机给你一个「西汉时期的武威」,让你在地图上点出位置,答完算分给等级。
这题真正难的地方在于:你可能记得「河西四郡」这个词,但说不出它们在今天的哪个位置。玩两轮,比看十遍疆域图记得牢。
最后
作者在自述里说,他上学时最爱翻历史课本里的地图,看大唐疆域幻想贞观开元,翻到宋朝为燕云十六州叹气。工作以后读《两晋悲歌》,五胡十六国那团乱麻怎么也拼不出地图,这才自己动手做。
今年 7 月他试过一版,做的是历史地理小游戏,做到一半觉得「我更想看的是那张地图本身」,就搁下了。10 月模型能力上来,他把项目捡回来,只做地图——两周半,两百多次提交,大半在深夜和周末。
评论区有一句话被顶得很高,我觉得比任何产品介绍都准:
拖动时间轴的那一刻,朝代不再是课本上的名词,而是脚下可以反复往返的山河。好工具让记忆有了坐标。
顺便说个数据:10 月 25 号作者写自述时,项目是 1400 star;一周多之后的今天,3,357。增长还在加速。
对我自己来说,这个项目最值得带走的不是那 164 张快照,而是那套「让 AI 传语义、不传坐标」的接口设计——做企业内部知识库、做数据可视化,思路是相通的。