news 2026/9/15 15:57:08

deck.gl ContourLayer 独立示例实战:从 COVID-19 数据到等值线/等值带可视化

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
deck.gl ContourLayer 独立示例实战:从 COVID-19 数据到等值线/等值带可视化

deck.gl ContourLayer 独立示例实战:从 COVID-19 数据到等值线/等值带可视化

【免费下载链接】deck.glWebGL2 powered visualization framework项目地址: https://gitcode.com/GitHub_Trending/de/deck.gl

本篇文章围绕 deck.gl 仓库中的 ContourLayer 最小独立示例(examples/website/contour)展开,完整讲解如何将示例复制到自己的项目、安装依赖并启动 Vite 开发服务器,同时结合示例代码与 ContourLayer API 文档、聚合层模块源码,深入剖析等值线(Isoline)与等值带(Isoband)的生成原理、全部核心属性(cellSizecontoursaggregationzOffset等)及底层实现。读完本文,你将能够独立运行该示例、理解 Marching Squares 算法如何被应用到聚合网格上,并把它改造成自己的等值线可视化应用。

示例概览:一个最小的 ContourLayer 应用

examples/website/contour是 deck.gl 官方 ContourLayer 示例的最小独立版本,目录结构非常精简,仅包含 5 个文件:

  • app.tsx—— 应用主逻辑,包含数据加载、ContourLayer 配置、底图与 Tooltip
  • index.html—— 入口 HTML,通过<script type="module">引入app.tsx
  • package.json—— 依赖与脚本声明(Vite 构建)
  • tsconfig.json—— TypeScript 配置
  • README.md—— 运行说明(即本文展开讲解的原始文档)

从 package.json 可以看到,示例基于 React 18 + Vite 构建,核心依赖为deck.gl@^9.0.0maplibre-gl@^5.0.0react-map-gl@^8.0.0,其中@deck.gl/aggregation-layers提供了本次主角ContourLayerindex.html加载了 maplibre-gl 的样式文件,并将挂载点指向<div id="app">,随后调用app.tsx导出的renderToDOM完成渲染。

该示例使用美国各县每周新增 COVID-19 病例数据(按每 10 万居民归一化),通过等值带(Isoband)与等值线(Isoline)两种形式直观展示疫情的空间分布态势。

快速开始:安装依赖并启动

示例 README 给出的运行流程非常直接:将整个文件夹内容复制到你的项目,然后执行安装与启动命令:

# 安装依赖 npm install # 或者使用 yarn yarn # 使用 Vite 打包并启动开发服务器 npm start

对应 package.json 中的脚本定义:

  • npm start等价于vite --open,启动开发服务器并自动打开浏览器;
  • npm run start-local使用仓库根目录下的vite.config.local.mjs配置,用于在 deck.gl monorepo 内以本地源码方式联调;
  • npm run build执行vite build产出生产构建。

如果你的项目尚未初始化,也可以参考 安装指南 单独安装所需模块:

npm install deck.gl # 或按需安装 npm install @deck.gl/core @deck.gl/layers @deck.gl/aggregation-layers

随后在代码中引入:

import {ContourLayer} from '@deck.gl/aggregation-layers'; import type {ContourLayerProps, ContourLayerPickingInfo} from '@deck.gl/aggregation-layers'; new ContourLayer<DataT>(...props: ContourLayerProps<DataT>[]);

示例数据格式:COVID-19 病例数据

示例使用的数据文件是covid-by-county.json,由app.tsx中的DATA_URL常量指向 deck.gl 官方示例数据仓库。每个数据对象的结构如下(对应app.tsx中的CaseReport类型):

type CaseReport = { state: string; // 州名 county: string; // 县名 longitude: number; // 经度 latitude: number; // 纬度 population: number; // 人口数 casesByWeek: {[week: number]: number}; // 按周索引的病例数,week 为周序号 };

app.tsx中,getPosition直接返回[d.longitude, d.latitude],而getWeight将某一周(week参数)的病例数除以人口再乘以1e5,得到"每 10 万居民的新增病例数"作为权重值:

getPosition: d => [d.longitude, d.latitude], getWeight: d => ((d.casesByWeek[week] || 0) / d.population) * 1e5,

更换自己的数据时,只需保证数据对象能通过getPositiongetWeight两个 accessor 提供坐标和权重即可,格式并不强制。更完整的字段说明可查看 ContourLayer 文档。

底图配置:CARTO 免费底图服务

示例的底图由 CARTO 免费底图服务提供,默认样式为https://basemaps.cartocdn.com/gl/dark-matter-gl-style/style.json(深色底图,与彩色等值带形成高对比)。在app.tsx中通过react-map-gl/maplibreMap组件嵌入:

<DeckGL initialViewState={INITIAL_VIEW_STATE} controller={true} layers={layers} getTooltip={getTooltip} style={{mixBlendMode: 'lighten'}}> <Map reuseMaps mapStyle={mapStyle} /> </DeckGL>

style={{mixBlendMode: 'lighten'}}使 deck.gl 渲染的等值带与底图以"变亮"模式混合,等值带颜色只会使底图变亮而不会压暗,视觉上更干净。示例的INITIAL_VIEW_STATE定位在全美范围(经度 -100、纬度 40、zoom 3、maxZoom 10)。如需替换为其他底图方案,可将mapStyle换成任意符合 MapLibre GL Style 规范的样式地址。

核心概念:等值线(Isoline)与等值带(Isoband)

ContourLayer的作用是将离散点数据聚合成连续场,再绘制出等值线或等值带。按官方文档的定义:

  • Isoline(等值线):一组把"高于某阈值"与"低于某阈值"区域分隔开的线段集合。生成它只需要一个阈值数值;
  • Isoband(等值带):填充某一阈值范围内区域的多边形集合。生成它需要一个包含两个数值的数组

数据的处理流程为:先用指定的cellSize(格网单元尺寸)对数据进行聚合,得到一个标量场(每个网格单元一个值),再对该标量场运行Marching Squares(行进方形)算法,生成构成等值线或等值带的顶点序列。文档与源码中统一将二者合称为"contour"。

在 contour-layer.ts 中,这一流程的调用链清晰可见:聚合结果通过getAggregatorValueReader封装为(x, y) => value的值读取函数,随后generateContoursbinIdRange(网格单元范围)内逐格遍历,对每个格子调用marching-squares.ts中的getCodegetLinesgetPolygons生成线段与多边形(详见下文源码分析)。

核心属性详解

以下属性均可直接从 contour-layer.ts 的defaultProps与 官方文档 中得到印证。

聚合选项

cellSize(number,可选,默认1000每个网格单元的边长,单位为米。示例中设为60000(60 公里),因为 COVID-19 数据覆盖全美,需要较大的单元才能形成平滑的场。文档中的自行车停放点示例则使用200(米)。属性声明中还包含min: 1的下限约束。

gpuAggregation(boolean,可选,默认true当为true且浏览器支持时,聚合在 GPU 上执行;否则回退到 CPU 聚合。在 contour-layer.ts 中通过WebGLAggregator.isSupported判断 GPU 可用性,不可用时自动使用CPUAggregator。两者各有取舍,详见下文"CPU vs GPU 聚合"章节。

aggregation(string,可选,默认'SUM'定义所有落入同一网格单元的数据点权重如何合并为该单元的值,合法取值包括:

  • 'SUM':落入单元的所有点权重之和
  • 'MEAN':落入单元的权重平均值
  • 'MIN':最小值
  • 'MAX':最大值
  • 'COUNT':落入单元的点数量

示例使用'MAX',即每个网格单元取该区域所有县中"每 10 万居民新增病例数"的最大值,从而避免单元内多县人口差异被求和掩盖。源码中该值会作为operations传入 aggregator(contour-layer.ts)。

渲染选项

contours(object[],可选,默认[{threshold: 1}]等值线/等值带配置数组。注意:与普通 prop 一样,contours采用浅比较判断是否变化,因此应将其作为数组对象整体传入,仅在需要变更时替换。每个配置项包含以下键:

类型说明
thresholdnumbernumber[2]等值线时为一个数值;等值带时为[min, max)区间,即渲染>= threshold[0]< threshold[1]的区域(下限包含、上限不包含)
colorColor(可选)RGBA 颜色数组,默认[255, 255, 255, 255];若只给 RGB 三分量,Alpha 自动取 255
strokeWidthnumber(可选)仅对等值线生效,线宽(像素),默认1
zIndexnumber(可选)等值线/带的 z 层级,值越大的渲染在越上层,用于重叠 contour 的精确排布,可避免 z-fighting;缺省时自动分配0n(contour 数量)的唯一值

示例中定义了两套 contour 配置,分别用于两种可视化模式:

// 等值带(BANDS):按病例密度从低到高、颜色从浅黄到深红渐变 export const BANDS = [ {threshold: [0.1, 1], color: [255, 255, 178]}, {threshold: [1, 10], color: [254, 204, 92]}, {threshold: [10, 100], color: [253, 141, 60]}, {threshold: [100, 500], color: [240, 59, 32]}, {threshold: [500, 2000], color: [189, 0, 38]}, {threshold: [2000, 10000], color: [159, 0, 80]} ]; // 等值线(LINES):六个阈值,统一的线宽 export const LINES = [ {threshold: 1, color: [255, 255, 178], strokeWidth: 2}, {threshold: 10, color: [254, 204, 92], strokeWidth: 2}, // ... 依次为 100、500、2000、10000 ];

这也是文档中官方示例(旧金山自行车停放点数据)的风格:多个等值线阈值配不同颜色与线宽、不同zIndex保证压盖顺序:

contours: [ {threshold: 1, color: [255, 0, 0], strokeWidth: 2, zIndex: 1}, {threshold: [3, 10], color: [55, 0, 55], zIndex: 0}, {threshold: 5, color: [0, 255, 0], strokeWidth: 6, zIndex: 2}, {threshold: 15, color: [0, 0, 255], strokeWidth: 4, zIndex: 3} ]

zOffset(number,可选,默认0.005为等值线/等值带每个顶点附加的极小 z 偏移,用于控制重叠 contour 的纵向排布。例如当等值线压在等值带上时,需要给等值线更大的偏移使其显示在上层,避免 z-fighting。

数据访问器

getPosition(Accessor,可选,默认object => object.position用于从每个数据对象中取位置坐标。

getWeight(Accessor,可选,默认1每个对象的权重。可以是常数(所有对象权重相同),也可以是函数(对每个对象调用取权重)。该值与aggregation共同决定网格单元的数值。

示例代码逐段解析

将 app.tsx 与 README 结合,可以看到一个完整示例包含的四个关键部分:

1. 数据与视图状态。DATA_URL指向 COVID-19 数据;INITIAL_VIEW_STATE定义全美视角。

2. Layer 配置。核心是contourscellSizeaggregation与两个 accessor 的组合:

new ContourLayer<CaseReport>({ data, id: 'contour-layer', getPosition: d => [d.longitude, d.latitude], getWeight: d => ((d.casesByWeek[week] || 0) / d.population) * 1e5, updateTriggers: {getWeight: week}, // week 变化时重新聚合 pickable: true, aggregation: 'MAX', contours, cellSize })

updateTriggers: {getWeight: week}是关键优化:切换周次时仅更新权重访问器,避免不必要的全量更新。

3. Tooltip 定制。通过info.object.contour.threshold判断命中的等值带区间,将阈值映射为人类可读文案:

const {threshold} = info.object.contour; if (threshold[1] === 1) str = '<1 new case'; else if (threshold[0] === 2000) str = '>2,000 new cases'; else str = `${threshold[0]}-${threshold[1]} new cases`;

并利用week反推周日期(以 2020-01-20 为第 0 周):

const date = new Date(Date.UTC(2020, 0, 20) + week * MS_PER_WEEK);

4. 地图容器。DeckGL内嵌 MapLibreMap组件,controller={true}启用交互(平移、缩放),style={{mixBlendMode: 'lighten'}}实现底图与等值带的混合显示。

渲染子图层与拾取

ContourLayer是复合层(Composite Layer),内部由两个子图层完成绘制(见 contour-layer.ts):

  • lines:等值线,由 PathLayer 渲染,getPath取顶点序列、getColor取 contour 颜色、getWidthstrokeWidthwidthUnits: 'pixels'
  • bands:等值带,由 SolidPolygonLayer 渲染,getPolygon取多边形顶点、getFillColor取填充色。

两个子图层共享同一个modelMatrix,它把网格坐标平移到cellOriginCommon并按cellSizeCommon缩放,再在 z 方向乘以zOffset,从而把生成在网格单元整数坐标系的顶点映射回真实地理空间。

拾取(Picking):hover/click 事件返回的PickingInfo.object代表一条等值线或一个等值带多边形,包含字段contour(即传入contours的对应配置项)。源码中的getPickingInfo(contour-layer.ts)会把拾取对象规整为{contour: ...},类型为ContourLayerPickingInfo

源码级原理:Marching Squares 与聚合

聚合 → 标量场

无论 CPU 还是 GPU,聚合的本质都是"把每个数据点分配到某个网格单元,并按aggregation操作合并单元值"。CPU 路径在 contour-layer.ts 中将经纬度投影到公共空间,按cellSizeCommon计算单元 ID;GPU 路径则通过project32模块在顶点着色器中完成相同的取整计算(contour-layer.ts)。

聚合完成后,getAggregatorValueReader(value-reader.ts)把聚合结果封装为getValue(x, y)读取函数,供 Marching Squares 按单元坐标取标量值。

Marching Squares 生成顶点

generateContours(contour-utils.ts)对binIdRange范围内的每个网格单元,根据四个角的单元值与阈值的比较结果生成 4 位/8 位二进制编码code,再查marching-squares-codes.ts中的偏移表生成线段或多边形:

  • 等值线:getVertexCodeweight >= threshold记 1,否则记 0,四个角构成 4 位编码,覆盖 16 种组合(含鞍点情形,需借助四角均值meanCode消歧);
  • 等值带:按weight < threshold[0]/< threshold[1]/ 其他分为 0/1/2 三态,构成 8 位编码,每个格子可能产出多个多边形;
  • 边界单元在范围外的一侧强制取 0,保证生成的 contour 正确闭合。

zIndex在此阶段被写入每个顶点坐标的 z 分量(contour-utils.ts),随后与子图层的modelMatrix一起实现三维分层。

增量更新机制

contour-layer.ts 展示了精心的缓存策略:

  • cellSizegridOriginaggregation或数据变化时,重新计算binIdRange并更新 aggregator;
  • contours变化时,不重新聚合,只把contourData置空以触发重算等值线(聚合结果aggregatedValueReader保持不变)。

这一机制在测试中得到了明确验证。

CPU vs GPU 聚合的取舍

gpuAggregation默认开启,但并非总是更优。聚合层 overview.md 的说明可作为选型依据:

  • 数据规模:CPU 聚合耗时与数据量大致线性;GPU 有初始化着色器与上传缓冲的前期开销,但数据量增大后边际成本很小。超过 10 万条数据时 GPU 明显更快,小数据量下 GPU 反而可能更慢;
  • 数据分布:CPU 内存占用与"含数据的单元数"成正比;GPU 与"全部可能单元"(含空格子)成正比,因此数据密集时 GPU 更有优势;
  • 扩展兼容DataFilterExtensionMaskExtension等 GPU 扩展仅与 GPU 聚合协同工作;
  • 精度:GPU 着色器只支持 32 位浮点,虽然层内做了补偿,但 GPU 与 CPU 结果未必完全一致(项目内有测试保证两者在可接受范围内一致);
  • 访问单元内点:GPU 聚合不暴露"某单元包含哪些数据点",如需点击单元列出其中的位置,应使用 CPU 聚合或自行过滤数据。

测试验证

仓库中 contour-layer.spec.ts 从多个角度验证了本示例所涉及的行为:

  • 同时渲染等值线与等值带时,子图层依次为PathLayerSolidPolygonLayer
  • 更新zOffset只影响渲染、不触发重新聚合(aggregatedValueReadercontourData保持不变);
  • 更新cellSize会触发重新聚合与 contour 重算;
  • 仅修改contours(如只保留等值线或只保留等值带)时聚合结果复用、只重算 contour 数据;
  • 切换gpuAggregation: false会创建新的 CPU aggregator 并重算全部数据。

这些测试与示例代码共同印证了上文描述的缓存与更新语义,是复现、修改示例时最可靠的参考。

小结

examples/website/contour是一个麻雀虽小五脏俱全的 ContourLayer 参考实现:它演示了数据接入(COVID-19 周病例)、聚合参数选择(cellSizeaggregation: 'MAX')、双模式 contour 配置(等值带 + 等值线)、动态更新(updateTriggers)、拾取 Tooltip 与底图混合的全部环节。配合 ContourLayer 文档 阅读源码 contour-layer.ts 与 marching-squares.ts,即可将这套模式迁移到天气、人口密度、信号覆盖等任意"离散点 → 连续场"的可视化场景中。

【免费下载链接】deck.glWebGL2 powered visualization framework项目地址: https://gitcode.com/GitHub_Trending/de/deck.gl

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/15 15:56:22

SAP固定资产导入实战:AS91与OASV全解析

做SAP FICO这些年&#xff0c;我最大的体会是&#xff1a;一个项目的成败&#xff0c;往往不在那些花里胡哨的增强开发&#xff0c;而在最不起眼的数据迁移环节。固定资产导入就是最典型的例子。AS91和OASV这两个事务码&#xff0c;几乎是每个SAP上线项目都会碰到的“固定组合”…

作者头像 李华
网站建设 2026/9/15 15:56:02

3个实战案例揭秘有域名和主机怎么做网站SEO

3个实战案例揭秘有域名和主机怎么做网站SEO 网站做好了没人访问,这种痛感比没建站更折磨人。我见过太多老板,域名主机都买好了,网站上线三个月,百度搜自己公司名字都排在第三页。问题出在哪?往往不是代码写得烂,而是从第一天起,SEO的底子就没打对。今天不聊虚的,直接拿三个我经手的真实 实战案例…

作者头像 李华
网站建设 2026/9/15 15:53:27

NINJITSU OS V3虚拟机安装实战:从下载到快照的完整指南

像NINJITSU OS这样的小众渗透发行版&#xff0c;能找到一份靠谱的安装教程确实不容易。这个系统在国外安全圈子里口碑不错&#xff0c;但国内讨论不多&#xff0c;很多朋友卡在下载、引导、虚拟机兼容这些环节就放弃了。我前前后后在VMware Workstation Pro和VirtualBox上都装过…

作者头像 李华