Lost Temple

地图聚类不是我的最爱:一封写给后 DOM 时代的技术债宣战书


全文翻译

地图聚类不是我的最爱

2026 年 6 月 12 日

朋友们,这篇文章也许酝酿了 20 年。

Google Maps(和我)都(至少)这么老了。当然,地图在它之前就已经存在了——那还是旧时光。

但大约 20 年前,或者那之后一年左右,有一样东西出现在了我们所有做地图的人的视野里,从那以后我就再也无法摆脱它——而且我从来没有在文字里、当着所有人的面抱怨过它,好让大家"享受"一下。享受吧!这是一篇 Greg 式咆哮(Greg Rant)!棒极了……

大约 20 年前,在 Google Maps 被强加给我们所有人之后,发生的事情是:“人们想在地图上看到不止 1 个点”,有时候这意味着 100 个点,或者一千个!那时候事情既不容易也不好。我是说,1000 个点,那可真多。

但是,是谁说 1000 个点(是你说的吗?)太多了没法显示?嗯……浏览器?那个年代古老、老朽(呃,只是没那么新)的浏览器倒不一定挣扎,它们更多是乞求/希望你别为地图上每一个想加的小点都创建一个字面意义上的 DOM 元素。

但那就是 Google Maps 当年做的事,2005-6-7-8-9 年(他们现在不这么干了)。

在 2005-6-7-……年加 1000 个点,意味着给你的地图加 1000 个 DOM 元素,而每个 DOM 元素里面还套着更多 DOM 元素。如果你是刷算法题那类人,你会说类似"O(N) 啊!!“的话,而且你说得对——回去学习吧。

于是,我们所有做地图的、搞地图的人、地图一族的,都接受并内化了"你最好别在那地图上显示太多信息!可能会崩!",并讨论、分享各种应对策略。挺好玩的。那会儿我们还没有 Instagram。

我至今还记得当年争论(并且反感)分页——你在地图上看到 10 个东西,然后点"下一页!"(地图的一页?)然后看到另外 10 个不同的点。今天我会说:这是糟糕的 UX。

或者你会用……热力图?或者……你把点烘焙(bake)进图片里,然后提供这些图片?你可以去查 WMS 是什么,但你不必查。

第二章。

曾经有一个美丽——真的是毫无讽刺意味的美丽——网站叫 Atlas Obscura,它告诉我们这个世界上有趣的地方在哪里。哦,看——一个美丽而独特的地方!就在这儿!还有关于它的历史。Atlas Obscura 很棒。

但是/然而,我要拿它当作这里要说的——我有意见的那个东西——的例子,是它/这张地图:

这张地图。

它是一张标注了 3 万多个地点的地图——全在一张地图上——这张地图展示了所有这些地点——但是聚类(clustered)显示的。

聚类就是那些里面带数字的圆圈。每个聚类……把更多条目隐藏 & 打包在自己内部。要发现那些条目,你必须放大。

于是,头号挫败感来了——聚类,和标记(marker)并存,但不告诉你任何信息。点击它们只是放大地图……因为……这是你必须做的事……也许能揭示聚类的内容。它就像健达奇趣蛋(Kinder Surprise),是一块巧克力,里面也许有个好玩的玩具,但你看不到玩具(巧克力不是透明的)。你这辈子正处于"在巧克力外面"的阶段。你必须更靠近那层巧克力壳才能进去拿到玩具,而此刻你看不见它,因为隔着一层巧克力。你听懂了吗?

于是,你点击。但是,聚类有时候也永远不会揭示它里面是什么。因为……如果一个聚类把多个点聚在一起,而这些点要么非常非常非常靠近(或者甚至,确实是位于同一位置的多个点——这种情况会发生),这个聚类就永远是一个封闭的盒子。(附带说明,这对非聚类地图也仍然是个问题。)

这就像有人送你一个套娃(matryoshka),但不允许你打开它——这就违背了套娃的意义与乐趣。那样的话它根本就不是套娃了,只是一份让人伤心的礼物。

然后,第二点——最滑稽的一点——是很多时候,放大一个聚类,在最后一刻,揭示出来的点根本不在聚类所在的位置。你放大,砰,它像原子一样分裂,你看到左边一个点、右边一个点。这说得通!它们被聚类、“平均"到一起了。但这是糟糕的 UI,令人挫败,以一种最糟糕的方式让人意外。

但是你知道,上面这些还不是对我 / 你 / 这个世界最重要的那些。

最真实的原因——最体面的、仇恨聚类的理由是:读一堆叠在地图上的数字,根本不是眼睛 / 大脑擅长的事。盯着一张布满数字的地图,是一种古怪的消遣,不具生产力、不高效等等——简单说,就是种负担。

地图聚类解决了一个问题(点太多、浏览器不喜欢)——好吧——也许吧——但是——那个问题已经不再是问题了。

因为……2023-4-5-6-年的地图技术惊人、快速,而且基于电子游戏技术。现代地图(你手机上的也是,哦太是了)都是杰作——科技的 GPU + shader 代码,旨在无限快地、并行地、聪明地、经济地渲染。它们缩放和平移无限快——是的,60fps,是的 120。更多。无所谓。我们已经超越帧率焦虑(post-fps)。而且绝对超越 DOM(post-DOM)。现在一切都快。

你不再需要聚类了。你可以就这么显示。

You don’t have to cluster anymore. You can just be.

第三章。

我下载了 Atlas Obscura 的数据,用 Protomaps 做了一张地图,用多种方式向你传达——这篇文章的"观点”。

其中一个(但不是默认的)模式,就是直接给你看所有该死的点。地图依然快速流畅,因为 MapLibre GL JS 是个杰作,所有参与者都做了了不起的、伟大的工作。我喜欢它的一切(不一定是我做的那张地图,我的意思是——带领我们走到今天、让这一切如此简单、如此快速、如此美丽的全部人类努力)。

但是同时——这张地图把聚类的 / 太多的 / “我们不能把所有东西都显示在地图上对吧"这些概念,以及其他一些考量,都拿来玩了一把。我觉得一个略微不透明的点叠加在其他点之上(这个模式——默认模式)也许是最利落的。它传达了密度,又让你看到点。hover 很满足(是的,仅桌面端)。我不主张到处都用热力图……但也许来点提示?我不知道。我到底知不知道自己喜欢什么?

第四章。

Atlas Obscura 没做错什么——它们很棒!——只是时候到了,我们该把聚类抛在身后了。它不邪恶,不是垃圾——它就是结束了。它终结了。我们不再需要聚类——尽管它有各种毛病,它在 20 年前、然后也许 15 年前、也许 10 年前,对我们是有用的。但我们现在处于后聚类(post-cluster)时代。我们可以不聚类,公开地。我们可以做回自己!

烧掉你的聚类。给我们看你的点。

Burn your clusters. Show us your points.


深度解读

这篇文章回答的问题: 地图上那些带数字的圆圈(聚类),是一个还在生效的工程必要,还是一个早已失去存在理由、却被惯性延续下来的技术债?

这篇文章应该回答但没回答的问题: 如果聚类的"性能"理由已经不成立,那它的"认知减负"理由是否也该一并清算?以及——在什么设备、什么密度、什么用户群体下,聚类其实仍然是对的?

一、这篇文章到底在吵什么:一次技术债的清算

别被 Greg 的咆哮体带偏了。剥掉情绪,这篇文章的内核是一个极其清晰的工程判断:

聚类(clustering)这个机制,是 2005 年浏览器性能约束下被迫做出的妥协,而那个约束已经在 2023 年后被现代渲染栈彻底瓦解。我们今天还在用聚类,不是因为它是好设计,而是因为它是惯例。

这是一个"技术债清偿"的经典案例。Greg 做的事,和那个"删掉一段没人敢动的代码、系统反而更快了"的故事是同一类——指出一个所有人都默认"必须存在"的东西,其实早已失去存在的前提。

只不过 Greg 用了 Kinder Surprise(健达奇趣蛋)和 matryoshka(俄罗斯套娃)的比喻,把它讲成了一个很好玩的 rant。

二、聚类的"原罪”:它从来不只是性能优化,它最初就是个补丁

Greg 的论证有一个很多人没意识到的关键前提,值得单独拎出来讲清楚。

回到 2005-2009 年的 Google Maps。它当时的渲染模型是这样的:地图上的每一个点(marker)= DOM 树里的一个节点。不是 canvas 上的一个像素,不是 GPU 的一个顶点,是一个货真价实的 <div><img>,挂在浏览器的 DOM 树上,参与布局(layout)、参与样式计算(style recalc)、参与事件分发(event dispatch)。

这意味着什么?显示 1000 个点 = 往 DOM 里塞 1000 个元素,每个还可能套着图标、阴影、标签等子元素,于是实际节点数可能是 3000-5000。而 DOM 操作的成本是出了名的高——每一次重排(reflow)都要遍历这些节点。这就是 Greg 说的"O(N)"。

当时的浏览器扛不住。于是地图社区发明了一系列"减少同时显示的元素数"的策略,聚类只是其中之一,完整的谱系是:

策略做法解决的是什么代价
分页(paging)地图上只显示 10 个点,点"下一页"换 10 个DOM 元素数量完全割裂空间连续性,UX 灾难
烘焙成图片(WMS)在服务器端把点画进一张栅格图,浏览器只显示图片DOM 元素数量 + 客户端计算无法交互、不能 hover、每次缩放重新请求
热力图(heatmap)用颜色梯度表示密度,不显示单个点DOM 元素数量 + 视觉杂乱丢失个体信息
聚类(clustering)把空间上邻近的点合并成带数字的圆圈,放大才展开DOM 元素数量(动态降低)隐藏信息、需要反复点击、点会"瞬移”

注意这张表的关键洞察:聚类当年被发明出来,第一目标就是"减少同时存在的 DOM 节点数",从而把 O(N) 降到 O(聚类数)。它的本质和分页、WMS 是同一个东西——都是在用"少显示一些"来换取"浏览器不崩"。

Greg 的全部论证建立在这个事实之上:既然那个"浏览器会崩"的前提已经没了,那么为了应对这个前提而发明出来的所有策略,逻辑上都该重新审视。 这一步推理是成立的,无可辩驳。

三、现代渲染栈到底换了什么:为什么"显示所有点"突然不要钱了

这是全文技术上最值钱的一句话,Greg 却一笔带过了——“modern maps are based on video game technology”。值得把它拆开讲明白。

现代地图(MapLibre GL JS、Mapbox GL、Google Maps 当代版本)的渲染管线,和 2005 年的 Google Maps 是两个完全不同物种

维度2005 Google Maps(DOM 时代)2026 现代地图栈(矢量瓦片 + GPU)
渲染载体DOM 元素(<div>/<img>WebGL / WebGPU canvas
数据格式栅格瓦片(PNG 图片) + DOM marker矢量瓦片(.mvt/.pbf,几何指令)
谁在画浏览器布局引擎(CPU)GPU(成百上千个并行核心)
复杂度模型O(N) 个 DOM 节点,每个参与布局O(屏幕像素),点再多个 GPU 也就那么多顶点
缩放/平移重新请求瓦片 + 重建 DOMshader 矩阵变换,几乎零成本
1000 个点的成本1000+ 个 DOM 节点的布局/事件开销GPU 一次性提交,几千个点对它来说是"零头"

这里有一个反直觉但极其重要的点,值得你记住:

GPU 渲染的成本主要取决于"屏幕上有多少像素要画",而不是"场景里有多少个几何体"。 当你把地图上的点画成一个 4×4 像素的小方块时,30000 个点如果彼此重叠,GPU 实际处理的像素数可能只有几千个。这就是为什么 Greg 能用 Protomaps + MapLibre 把 3 万个点全部画出来,还保持 60fps——不是因为魔法,而是因为渲染模型从"每个点一个 DOM 节点"变成了"GPU 一次性画一堆像素"。

这是范式转移(paradigm shift),不是渐进改良。2005 年的约束和 2026 年的约束,底层物理都不一样了。

地图聚类解决了一个问题(点太多、浏览器不喜欢)——好吧——也许吧——但是——那个问题已经不再是问题了。

Map clustering solves a problem… but also… that problem is not a problem anymore.

四、Protomaps / MapLibre 是什么:Greg 手里那把"新武器"

Greg 提了两个工具,这里给一个清晰的定位,免得你以为这是什么冷门小众货。

这两个东西组合在一起,就是 Greg 整篇咆哮的物质基础:“显示所有点"在 2026 年已经从一个需要工程妥协的难题,变成了一个下午就能做完的 demo。

五、压力测试:Greg 说对了一半,另一半他偷换了概念

现在做我必须做的批判性检验。Greg 的核心论点(“聚类的性能理由已死,所以聚类该死”)逻辑链有一个隐藏的跳跃,我必须给你拆穿:

他把"聚类的性能动机"等同于"聚类的全部价值”,然后用前者过时来否定后者。

这是稻草人谬误。聚类其实服务于两个不同的目标:

  1. 性能目标(减少 DOM 节点 / 减少绘制开销)——这个确实在现代栈里基本失效了。Greg 说对了。
  2. 认知目标(减少视觉杂乱、帮助用户感知宏观分布模式)——这个和渲染速度无关,是人因工程学(human factors)问题。Greg 几乎没正面回应它。

他唯一一次碰触认知目标,是第三章那句"略微不透明的点叠加能传达密度”——但他没意识到,半透明叠加本身也是一种降密度策略,和聚类是同一棵树上的不同枝。他一边喊"烧掉聚类",一边用半透明叠加这种温和版的聚类来兜底,这恰恰说明:当点密度高到一定程度,“原样显示所有点"在认知上仍然是过载的,你总得做点什么。

地图可视化领域有大量关于 visual clutter(视觉杂乱) 的研究,结论相当一致:当屏幕上重叠的点超过某个阈值(通常经验值是几百到上千),未聚类的散点图会让用户定位特定目标的反应时间和错误率显著上升。这个结论和你的 GPU 快不快无关——GPU 再快,人眼的处理带宽也是有限的。

所以更准确的说法应该是:

Greg 的说法更准确的说法
“聚类该被烧掉”“为性能而做的聚类该被烧掉;为认知减负而做的聚类需要重新设计”
“现代技术让显示所有点变得免费”“现代技术让显示所有点在性能上免费,但在认知上从不免费”
“烧掉你的聚类,显示你的点”“先问自己:我的用户是在找某个具体的点,还是在感知整体分布?前者烧聚类,后者聚类/热力/密度图仍有价值”

还有一个 Greg 完全没提的维度——低端设备。他口中的"现代技术很快"是建立在高端手机和桌面 GPU 之上的。但地球上还有大量低端 Android、老旧企业终端、电量受限的移动场景,在那些设备上,渲染 30000 个矢量点 + 持续响应缩放,仍然是有真实开销的。聚类在这些场景里不只是性能补丁,而是节能和可用的保障

你不再需要聚类了。你可以就这么显示。

You don’t have to cluster anymore. You can just be.

这句话作为技术宣言很燃,但作为工程建议是危险的。“can”(可以)和"should”(应该)之间,隔着一整个用户群体的设备现实。

六、决策框架:什么时候烧聚类,什么时候留着

把上面的批判收敛成一个可用的判断表。下次你在地图项目里纠结要不要聚类,照着这个走:

你的场景用户的核心任务推荐做法
点数 < 500,桌面端为主找具体点 / 浏览直接显示所有点,不聚类(性能完全够)
点数 500-5000,桌面+移动找具体点直接显示,移动端用半透明叠加降视觉杂乱
点数 > 5000,或密度极高感知整体分布 / 找热点热力图或密度可视化,聚类作为缩放后的过渡
低端设备 / IoT 显示屏 / 弱网任何聚类或瓦片化仍然是合理的,性能预算优先
用户需要逐一查看每个点的详情精确查询永远不要聚类——它会变成永远打不开的套娃
同一坐标有多个点(如多家门店同址)去重/查看聚类或列表展开,但必须有"列表视图"兜底,否则就是 Greg 说的"封闭盒子"

记住一句话:聚类是工具,不是宗教。 Greg 的问题是把它当宗教来信,而你该做的是当工具来用。

七、对你的启发:把 Greg 的逻辑移植到你自己的技术债上

Greg 这篇文章最大的价值,不是地图聚类本身(你可能这辈子都不做地图),而是它示范的一种思维方法:

定期审视你系统里那些"大家都这么做"的机制,追问一句——它最初是为了解决什么问题?那个问题今天还存在吗?

你会发现,大量"理所当然"的设计,都是某个早已消失的约束的化石。聚类是 2005 年浏览器的化石。你的代码库里一定也有类似的:某个为了兼容 IE8 的 polyfill、某个为了绕过老版本数据库 bug 的 workaround、某个"必须先调 A 再调 B"的顺序约束。它们都曾是必要,但必要的前提可能早就没了。

烧掉你的聚类。给我们看你的点。

Burn your clusters. Show us your points.

把这句话里的"聚类"换成你自己系统里那个"没人敢删但没人记得为什么"的东西,它同样成立。


苏格拉底对话:那 3 万个点,到底该不该一次性全显示出来?

尾巴:Greg 说得挺带劲啊——现代 GPU 这么快,3 万个点直接画不就完了,为什么还要搞聚类这种让人点到手酸的玩意儿?

Cloudcold:先别急着认同。我问你一个问题:如果我把 3 万个点,全部用同一个颜色的实心小圆点画在你屏幕上,你第一眼能看到什么?

尾巴:呃……一团糊?大概是某些区域密集、某些区域稀疏?

Cloudcold:对,你看到的是"密度",不是"点"。那我问你第二个问题:现在这团糊里,你想找到某个具体的点——比如北京三里屯那家——你找得到吗?

尾巴:找不到,全糊在一起了。

Cloudcold:这就是关键。当你把所有点无脑画出来,你的 GPU 确实很开心,但你的眼睛不开心。Greg 说的"现代技术让显示所有点变得免费",这句话在性能上是真的,在认知上是个谎言。你刚才那个"一团糊"的体验,就是认知过载。

尾巴:那 Greg 自己不也用了"半透明叠加"吗?他不是也承认要降密度?

Cloudcold:你抓到要害了。半透明叠加——就是让重叠的点变得更深的颜色——它本质上是聚类的温和版亲戚。聚类是"把多个点合并成一个数字",半透明叠加是"让多个点的颜色叠加反映密度"。它们解决的是同一个问题:点太多,人眼处理不过来。 Greg 一边喊烧掉聚类,一边偷偷用聚类的小弟,这恰恰证明了他想否定的那个问题——视觉杂乱——是真实存在的。

尾巴:所以聚类的真正理由不是"浏览器会崩",而是"人会崩"?

Cloudcold:完全正确。2005 年的聚类,确实主要是为了救浏览器。但聚类能活到今天,是因为它顺带也救了人眼。Greg 只攻击了前一半,对后一半视而不见。这不是一个小遗漏——它决定了"烧不烧聚类"这个问题的真实答案。

尾巴:那我到底该怎么判断?什么时候直接画,什么时候聚类?

Cloudcold:用一个简单的二分法。问自己:我的用户是在’找一个具体的点’,还是在’看整体分布’? 如果是前者——比如找一个门店、一个地址——聚类是灾难,因为它会让你永远点不到目标。如果是后者——比如看全国哪里的疫情更密集、哪里的订单更集中——聚类和热力图反而比裸点更有效,因为它们把"密度"这个信息前置了,不用你眯着眼数点。工具该匹配任务,不是工具该站队信仰。

尾巴:懂了。所以 Greg 的结论要打折,但他的方法论值得偷——定期问"这个机制当初解决的那个问题,今天还在吗"。

Cloudcold:就是这个。聚类只是一个具体的化石。你代码库里还有几十个类似的化石在等着你问同样的问题。最后一个留给你想的:如果聚类的性能理由确实死了,但认知理由还活着,那"后聚类时代"的理想形态,应该长什么样? Greg 给了半透明叠加一个暗示,但这远不是终点——这是一个开放问题,留给真正做地图产品的人去回答。


个性化洞察:把 Greg 的逻辑搬到你手上

基于你的身份(QA 背景 + 全栈 + 重度关注 AI 工具链 + TypeScript/Node/Rust),这几点直接能用:

  1. Greg 做的事,本质是"技术债考古"——这也是高级 QA / 工程师的核心技能之一。 你做 QA 最擅长的就是追问"这个行为/约束/默认值是为什么存在的",Greg 做的不过是把这个追问用到了地图聚类这个 20 年的老化石上。你可以把这套"约束溯源"方法固化成一个 checklist,用在你的项目 review 里:列出所有"大家都这么做"的机制,逐一标注它最初解决的约束,然后判断该约束今天是否还在。这比泛泛的"重构"精准得多。

  2. AI 工具链里正堆满了和"聚类"一样的化石。 比如 prompt 里的"你必须一步一步思考"(Chain-of-Thought)——它最初是为了弥补早期模型推理弱,但现在的强模型(Fable 5 / Opus 4.8)很多时候不需要这个脚手架,强行加反而拉长输出、增加成本。再比如很多 agent 框架里"必须先 plan 再 execute"的硬约束,源于早期模型上下文短,现在 1M context 时代也该松动。你完全可以用 Greg 的视角写一篇《你 prompt 里的那些聚类》。这是个现成的内容选题。

  3. “GPU 渲染成本取决于像素而非几何体数量"这个洞察,对你做 AI 产品的前端可视化有直接价值。 如果你要在前端可视化大规模数据(比如 token 流、向量空间、用户行为热力),别再用 DOM 元素一个一个堆——直接上 canvas/WebGL,用 GPU 思维。这是为什么很多现代数据可视化库(deck.gl、Observable Plot 的 WebGL 后端)都转向 GPU 渲染。你的 TS/Rust 技能栈正好适合啃这块。

  4. Greg 踩了一个你该避免的坑:用"can"替代"should”。 “技术上能做到"和"应该这么做"是两回事。你做 AI 产品时最容易犯的错就是"因为模型能做到,所以我就让它做”——比如让 LLM 处理超长上下文、让它一次性返回海量结构化数据。技术上可行,但在成本、延迟、用户体验上未必最优。永远把"can"和"should"分开问,这是工程判断力的分水岭。

  5. 一个可以直接落地的实验:用 Protomaps 自己跑一张"全点地图"。 Greg 那张 demo 的门槛已经低到"下载 PMTiles 文件 + MapLibre 一个 HTML 文件"。你周末花两小时就能复现,亲身体验一次"后 DOM 时代"的地图渲染。这种"亲手验证一个技术论断"的习惯,是区分技术消费者和技术创造者的关键——Greg 之所以敢这么笃定地 rant,是因为他真的下载了数据、真的做出了秒开的 demo,而不是嘴上说说。

#地图可视化 #技术债 #WebGL #MapLibre #前端架构 #Protomaps