deck.gl HexagonLayer 深入解析:六边形聚合热力图图层的原理与实战
deck.gl HexagonLayer 深入解析六边形聚合热力图图层的原理与实战【免费下载链接】deck.glWebGL2 powered visualization framework项目地址: https://gitcode.com/GitHub_Trending/de/deck.glHexagonLayer 是 deck.gl 聚合图层家族Aggregation Layers中的核心成员它把海量散点数据按地理空间聚合到正六边形hexagon网格中以六边形热力图的形式同时呈现数据密度、数值分布与 3D 高度。本文基于 docs/api-reference/aggregation-layers/hexagon-layer.md 展开结合仓库源码与测试完整讲解它的安装方式、全部配置属性、CPU/GPU 两种聚合模式的选择、拾取Picking信息结构与内部实现原理帮助你快速构建可上线的六边形聚合可视化应用。什么是 HexagonLayerHexagonLayer将输入数据聚合为基于六边形的热力图每个六边形的颜色与**高度拉伸高度**由落在其中的数据对象决定。它属于 CompositeLayer复合图层内部由一个名为cells的子图层完成最终渲染详见 Sub Layers 一节。与 GridLayer正方形网格不同HexagonLayer 使用**尖顶朝上pointy-topped**的六边形网格相邻六边形共享边视觉上更平滑、无缝隙非常适合密度型热力图场景。该图层在官网文档中标注支持 WebGPU。安装与引入从 npm 安装依赖npm install deck.gl # 或者按需安装 npm install deck.gl/core deck.gl/layers deck.gl/aggregation-layers按模块引入import {HexagonLayer} from deck.gl/aggregation-layers; import type {HexagonLayerProps, HexagonLayerPickingInfo} from deck.gl/aggregation-layers; new HexagonLayerDataT(...props: HexagonLayerPropsDataT[]);使用预打包脚本Pre-bundled scriptsscript srchttps://unpkg.com/deck.gl^9.0.0/dist.min.js/script !-- or -- script srchttps://unpkg.com/deck.gl/core^9.0.0/dist.min.js/script script srchttps://unpkg.com/deck.gl/layers^9.0.0/dist.min.js/script script srchttps://unpkg.com/deck.gl/aggregation-layers^9.0.0/dist.min.js/scriptnew deck.HexagonLayer({});仓库中提供了一个可直接运行的脚本式示例 examples/gallery/src/hexagon-layer.html它通过滑块实时调整radius、coverage、upperPercentile加载英国 3D 热力图数据d3.csv读取经度/纬度是快速体验 HexagonLayer 交互效果的参考模板。快速上手三个最小示例以下示例基于旧金山自行车停车位数据每行含COORDINATES坐标与SPACES车位数展示 HexagonLayer 最基本的用法按位置聚合、按车位数着色与拉高、开启 GPU 聚合与拾取。JavaScript纯 JS Deck 类import {Deck} from deck.gl/core; import {HexagonLayer} from deck.gl/aggregation-layers; const layer new HexagonLayer({ id: HexagonLayer, data: https://raw.githubusercontent.com/visgl/deck.gl-data/master/website/sf-bike-parking.json, gpuAggregation: true, extruded: true, getPosition: d d.COORDINATES, getColorWeight: d d.SPACES, getElevationWeight: d d.SPACES, elevationScale: 4, radius: 200, pickable: true }); new Deck({ initialViewState: { longitude: -122.4, latitude: 37.74, zoom: 11 }, controller: true, getTooltip: ({object}) object Count: ${object.elevationValue}, layers: [layer] });TypeScript带泛型与拾取类型import {Deck} from deck.gl/core; import {HexagonLayer, HexagonLayerPickingInfo} from deck.gl/aggregation-layers; type BikeRack { ADDRESS: string; SPACES: number; COORDINATES: [longitude: number, latitude: number]; }; const layer new HexagonLayerBikeRack({ id: HexagonLayer, data: https://raw.githubusercontent.com/visgl/deck.gl-data/master/website/sf-bike-parking.json, gpuAggregation: true, extruded: true, getPosition: (d: BikeRack) d.COORDINATES, getColorWeight: (d: BikeRack) d.SPACES, getElevationWeight: (d: BikeRack) d.SPACES, elevationScale: 4, radius: 200, pickable: true }); new Deck({ initialViewState: { longitude: -122.4, latitude: 37.74, zoom: 11 }, controller: true, getTooltip: ({object}: HexagonLayerPickingInfoBikeRack) object Count: ${object.elevationValue}, layers: [layer] });Reactimport React from react; import {DeckGL} from deck.gl/react; import {HexagonLayer, HexagonLayerPickingInfo} from deck.gl/aggregation-layers; type BikeRack { ADDRESS: string; SPACES: number; COORDINATES: [longitude: number, latitude: number]; }; function App() { const layer new HexagonLayerBikeRack({ id: HexagonLayer, data: https://raw.githubusercontent.com/visgl/deck.gl-data/master/website/sf-bike-parking.json, gpuAggregation: true, extruded: true, getPosition: (d: BikeRack) d.COORDINATES, getColorWeight: (d: BikeRack) d.SPACES, getElevationWeight: (d: BikeRack) d.SPACES, elevationScale: 4, radius: 200, pickable: true }); return DeckGL initialViewState{{ longitude: -122.4, latitude: 37.74, zoom: 11 }} controller getTooltip{({object}: HexagonLayerPickingInfoBikeRack) object Count: ${object.elevationValue}} layers{[layer]} /; }三个示例的逻辑完全一致gpuAggregation: true启用 GPU 聚合extruded: true开启 3D 柱体radius: 200决定六边形半径米elevationScale: 4放大柱体高度tooltip 中直接读取拾取对象的elevationValue展示聚合值。HexagonLayerPickingInfoDataT类型定义在 modules/aggregation-layers/src/hexagon-layer/hexagon-layer.ts包含col/row/colorValue/elevationValue/count等字段详见下文拾取一节。属性Properties全解HexagonLayer 继承自 Base Layer 与 CompositeLayer 的全部属性如id、data、pickable、transitions、updateTriggers、visible等并新增以下专属属性。它们在源码中的默认值声明位于 modules/aggregation-layers/src/hexagon-layer/hexagon-layer.ts 的defaultProps中下文默认值均与该文件一致。聚合选项Aggregation OptionsgpuAggregationboolean可选默认值true为true时聚合在 GPU 上完成。在合适的场景下 GPU 聚合能显著提升性能但受输入数据性质与所需功能影响各有利弊详见 CPU vs GPU 聚合对比。以下情况会自动回退到 CPU 聚合当前浏览器/设备不支持 GPU 聚合定义了hexagonAggregator定义了getColorValue定义了getElevationValue从源码看这一回退逻辑由getAggregatorType()实现hexagon-layer.ts当gpuAggregation为真但同时传入了hexagonAggregator/getColorValue/getElevationValue时会输出警告Features not supported by GPU aggregation, falling back to CPU并返回cpu否则在WebGLAggregator.isSupported(this.context.device)通过时返回gpu。测试 test/modules/aggregation-layers/hexagon-layer.spec.ts 专门验证了默认走 GPU、gpuAggregation: false走 CPU、传入getColorValue/getElevationValue回退 CPU 四条路径。radiusnumber可选默认值1000六边形的半径单位米。六边形为尖顶朝上pointy-topped而非平顶朝上。源码中该属性约束为min: 1hexagon-layer.ts。colorAggregationstring可选默认值SUM定义如何聚合落在同一六边形内所有数据对象的权重从而计算该六边形的颜色值。合法值SUM六边形内所有点的权重之和MEAN六边形内所有点的权重平均值MIN六边形内所有点的权重最小值MAX六边形内所有点的权重最大值COUNT六边形内点的数量getColorWeight与colorAggregation共同决定每个六边形的颜色值一旦提供了getColorValue这两个属性将被忽略。示例一按数据元素数量着色const layer new HexagonLayerBikeRack({ //... getColorValue: (points: BikeRack[]) points.length });const layer new HexagonLayerBikeRack({ // ... getColorWeight: 1, colorAggregation: COUNT });示例二按 SPACES 字段的均值着色const layer new HexagonLayerBikeRack({ // ... getColorValue: (points: BikeRack[]) { // 计算均值 return points.reduce((sum: number, p: BikeRack) sum p.SPACES, 0) / points.length; } });const layer new HexagonLayerBikeRack({ // ... getColorWeight: (point: BikeRack) point.SPACES, colorAggregation: MEAN });如果需要使用内置colorAggregation之外的聚合运算例如加权平均、标准差等则应使用getColorValue定义自定义聚合函数。elevationAggregationstring可选默认值SUM定义如何聚合六边形内所有数据对象的权重从而计算六边形的高度值。合法值与colorAggregation一致SUM、MEAN、MIN、MAX、COUNT。getElevationWeight与elevationAggregation共同决定每个六边形的高度值一旦提供了getElevationValue这两个属性将被忽略。示例一按数据元素数量决定高度const layer new HexagonLayerBikeRack({ // ... getElevationValue: (points: BikeRack[]) points.length });const layer new HexagonLayerBikeRack({ // ... getElevationWeight: 1, elevationAggregation: COUNT });示例二按 SPACES 字段的最大值决定高度const layer new HexagonLayerBikeRack({ // ... getElevationValue: (points: BikeRack[]) { // 计算最大值 return points.reduce((max: number, p: BikeRack) p.SPACES max ? p.SPACES : max, -Infinity); } });const layer new HexagonLayerBikeRack({ // ... getElevationWeight: (point: BikeRack) point.SPACES, elevationAggregation: MAX });同理需要内置运算之外的聚合逻辑时使用getElevationValue自定义。hexagonAggregatorFunction可选默认值null自定义点如何分组成六边形格子的函数。一旦提供该属性无论gpuAggregation设置为何值GPU 聚合都会被禁用这正是上文getAggregatorType()中的回退条件之一。该函数被调用时接收以下参数positionnumber[]——一个数据点的位置radiusnumber——radius属性的值它应返回一个包含 2 个整数的数组表示一个六边形 ID解析到相同六边形 ID 的点会被分到同一组。默认情况下HexagonLayer 使用 d3-hexbin 作为hexagonAggregator的实现基础对应源码为 modules/aggregation-layers/src/hexagon-layer/hexbin.ts文件头注明 Adapted from d3-hexbin。在 CPU 路径中createAggregator(cpu)会优先调用用户传入的hexagonAggregator(positions, radius)否则将坐标投影到公共空间后调用pointToHexbinhexagon-layer.ts。渲染选项Render Optionscoveragenumber可选默认值1六边形半径倍率取值范围被钳制在 0–1 之间。六边形的显示半径为coverage * radius。注意coverage不影响数据如何分箱binning只影响显示大小。源码约束min: 0, max: 1。extrudedboolean可选默认值false是否将六边形渲染为 3D 柱体。为false时所有六边形都是扁平flat的。开启后配合material、elevationScale可呈现 3D 热力图效果。colorScaleTypestring可选默认值quantize颜色比例尺将连续的数值区间colorDomain映射为一组颜色colorRange。值为colorDomain[0]的六边形渲染为colorRange[0]的颜色值为colorDomain[1]的六边形渲染为colorRange[colorRange.length - 1]的颜色。colorScaleType决定数值如何映射到颜色支持linear根据值在colorDomain中的位置对colorRange做线性插值quantize把colorDomain等分为colorRange.length段每段映射到colorRange中的一个离散颜色quantile把输入值按数量等分为colorRange.length组每组映射到colorRange中的一个离散颜色ordinal每个唯一值映射到colorRange中的一个离散颜色注意GPU 聚合下使用quantile或ordinal会产生一次从 GPU 读回 CPU 的聚合值开销如果源数据更新频繁这一开销可能不可取。ScaleType类型定义于 modules/aggregation-layers/src/common/types.ts。colorDomainnumber[2]可选默认值null自动未提供时图层在运行时将colorDomain设为所有六边形实际聚合值的最小/最大值。手动指定colorDomain可以控制数值到颜色的映射关系常用于让不同输入数据使用同一套颜色映射以便对比。colorRangeColor[]可选默认值ColorBrewer 的 6 级YlOrRd配色以颜色数组[color1, color2, ...]指定每个颜色是 3 或 4 个值的数组[R, G, B]或[R, G, B, A]分别表示红、绿、蓝、透明度通道各通道取值范围 0–255省略 Alpha 时按 255 处理。例如 examples/gallery/src/hexagon-layer.html 中的青-黄-红渐变配色const COLOR_RANGE [ [1, 152, 189], [73, 227, 206], [216, 254, 181], [254, 237, 177], [254, 173, 84], [209, 55, 78] ];elevationScaleTypestring可选默认值linear高度比例尺将连续的数值区间elevationDomain映射到另一段连续数值区间elevationRange。值为elevationDomain[0]的六边形渲染为elevationRange[0]的高度值为elevationDomain[1]的六边形渲染为elevationRange[1]的高度。支持的值linear根据值在elevationDomain中的位置对elevationRange线性插值quantile输入值按百分位分组每组映射到elevationRange中的一个离散高度elevationDomainnumber[2]可选默认值null自动未提供时图层在运行时将elevationDomain设为所有六边形实际聚合值的最小/最大值。手动指定可控制数值到高度的映射同样适用于多份数据统一高度标尺的对比场景。注意源码注释中其自动值语义为[0, max(elevationWeight)]hexagon-layer.ts。elevationRangenumber[2]可选默认值[0, 1000]高度比例尺的输出区间。elevationScalenumber可选默认值1六边形高度的倍率是不更新数据、只缩放所有柱体的便捷属性源码约束min: 0。示例中常配合extruded: true使用如 gallery 示例中的elevationScale: 250。upperPercentilenumber可选默认值100按upperPercentile过滤六边形并重算颜色。聚合值大于该百分位的六边形将被隐藏。GPU 聚合下使用该属性会带来一次 GPU 读回 CPU 的开销源数据更新频繁时需谨慎。lowerPercentilenumber可选默认值0按lowerPercentile过滤六边形并重算颜色。聚合值小于该百分位的六边形将被隐藏。GPU 聚合下的读回开销同上。elevationUpperPercentilenumber可选默认值100按elevationUpperPercentile过滤六边形并重算高度。高度值大于该百分位的六边形将被隐藏。GPU 聚合下同样存在读回开销。elevationLowerPercentilenumber可选默认值0按elevationLowerPercentile过滤六边形并重算高度。高度值小于该百分位的六边形将被隐藏。GPU 聚合下同样存在读回开销。提示以上四个 Percentile 属性与getColorWeight/getElevationWeight等带 transition 标记的属性均支持属性过渡transition动画详见 基础图层属性中的 transitions。materialMaterial可选默认值true一个包含材质属性的对象用于作用在拉伸多边形上的光照效果。可配置项见 光照使用指南中的 Material Settings。仅在extruded: true时生效。数据访问器Data AccessorsgetPositionAccessorPosition可选默认值object object.position获取每个对象位置的访问器。源码中该属性在 attribute manager 中注册为size: 3、type: float64并支持 64 位精度位置fp64见 hexagon-layer.ts。getColorWeightAccessornumber可选默认值1用于计算六边形颜色值的单个数据对象权重。传入数字所有对象使用同一权重传入函数对每个对象调用以获取其权重getColorValueFunction可选默认值null数据对象被分入六边形格子后对该格子调用此访问器以获得颜色所依据的值。若提供将覆盖getColorWeight与colorAggregation的效果。仅支持 CPU 聚合。参数objectsDataT[]——位置落在该六边形内的对象列表objectInfoobject——包含以下字段indicesnumber[]——objects在原始数据中的下标data——data属性的值对应的类型定义AggregateAccessor见 modules/aggregation-layers/src/common/types.ts。getElevationWeightAccessornumber可选默认值1用于计算六边形高度值的单个数据对象权重。传入数字所有对象使用同一权重传入函数对每个对象调用以获取其权重getElevationValueFunction可选默认值null数据对象被分入六边形格子后对该格子调用此访问器以获得高度所依据的值。若提供将覆盖getElevationWeight与elevationAggregation的效果。仅支持 CPU 聚合。参数与getColorValue一致objectsDataT[]与objectInfo含indices、data。回调CallbacksonSetColorDomainFunction可选默认值([min, max]) {}当六边形颜色域计算完成时被调用。从源码看该回调在_onAggregationUpdate中随聚合结果一同触发hexagon-layer.ts可用于将自动计算的 domain 同步到 UI 或外部状态。onSetElevationDomainFunction可选默认值([min, max]) {}当六边形高度域计算完成时被调用行为同上。深入理解 CPU vs GPU 聚合这是 HexagonLayer 性能调优最关键的决策点。官方在 聚合图层总览 中给出了系统性的对比结论以下是需要权衡的要点兼容性GPU 聚合所需的客户端特性已被主流常青浏览器普遍支持覆盖全球 95% 市场但个别设备/芯片的驱动差异可能影响结果。数据规模CPU 聚合耗时基本随输入数据量线性增长GPU 聚合需要着色器搭建与缓冲上传的前期开销但处理更多数据的边际成本很小。大数据集100K 点下 GPU 远快于 CPU小数据集下 GPU 可能反而更慢。数据分布CPU 聚合内存占用与至少包含一个点的格子数成正比GPU 聚合内存占用与所有可能格子含空格子成正比。数据密集集中时 GPU 表现更好稀疏分散时 GPU 不占优。过滤扩展基于 GPU 的扩展如 DataFilterExtension、MaskExtension 只能与 GPU 聚合配合使用。精度GPU 着色器仅支持 32 位浮点。图层实现了精度损失的缓解措施但 GPU 聚合与 CPU 聚合结果不完全一致是可预期的仓库有相应测试保证两者一致性在可接受范围内。格子内点访问GPU 聚合不暴露某格子具体包含哪些数据点。若需要此能力例如选中格子后列出其中的位置必须使用 CPU 聚合或自行按需过滤数据。官方在文档中给出的随机数据性能基准测试环境2016 款 15 英寸 MacBook ProCPU 2.8 GHz Intel Core i7GPU AMD Radeon R9 M370X 2 GB对象数量CPU 迭代/秒GPU 迭代/秒说明25K535359GPU 慢 33%100K119437GPU 快 267%1M12.7158GPU 快 1144%以上为官方文档记录的特定硬件环境下的测试数据实际表现请以目标运行环境实测为准。从源码看两种模式统一通过CPUAggregator/WebGLAggregator抽象modules/aggregation-layers/src/common/aggregator驱动CPU 路径逐点调用pointToHexbin计算 bin IDGPU 路径则把同样的算法以 GLSL 形式pointToHexbinGLSL见 hexbin.ts编译进顶点着色器在 GPU 上完成分箱与SUM/MEAN/MIN/MAX/COUNT归约。仓库测试 test/modules/aggregation-layers/hexbin.spec.ts 通过BufferTransform在 GPU 上运行同一算法验证 CPU 与 GPU 的pointToHexbin、getHexbinCentroid结果在1e-7精度内一致并同时与 d3-hexbin 的参考实现做了对拍。拾取Picking开启pickable: true后hover/click 事件返回的 PickingInfo.object 表示一个聚合后的六边形包含以下字段colnumber——被拾取六边形的列索引rownumber——被拾取六边形的行索引colorValuenumber——聚合颜色值由getColorWeight与colorAggregation决定elevationValuenumber——聚合高度值由getElevationWeight与elevationAggregation决定countnumber——被拾取六边形内的数据点数量pointIndicesnumber[]——六边形内数据对象的下标。仅 CPU 聚合可用pointsobject[]——六边形内的数据对象。仅 CPU 聚合且图层数据为数组时可用该拾取信息在源码的getPickingInfo中构造hexagon-layer.ts通过aggregator.getBin(index)取得 bin 后计算六边形质心getHexbinCentroid作为position并附带col/row/colorValue/elevationValue/count仅当 bin 携带pointIndices即 CPU 聚合时才填充pointIndices与points。对应 TypeScript 类型为HexagonLayerPickingInfoDataT。子图层Sub LayersHexagonLayer 渲染以下子图层cells——一个 ColumnLayer负责绘制聚合后的六边形柱体子图层由renderLayers()创建hexagon-layer.ts使用getSubLayerClass(cells, HexagonCellLayer)获得子图层类将聚合器的 bin 属性getBin与经比例尺缩放后的颜色/高度属性getColorValue、getElevationValue以实例化属性传入。实际渲染类 HexagonCellLayer 继承自 ColumnLayer通过diskResolution: 6与六边形顶点表HexbinVertices6 个顶点见 hexbin.ts把圆柱体替换为正六边形棱柱它还负责把colorRange生成颜色纹理createColorRangeTexture供着色器采样并在绘制时应用coverage、extruded、elevationScale、percentile 裁剪域等参数。该模块同时提供 WebGLhexagon-cell-layer-vertex.glsl.ts与 WebGPUhexagon-cell-layer.wgsl.ts两套着色器实现。内部实现机制速览理解 HexagonLayer 的渲染管线能帮你更准确地预测性能与调优方向分箱Binning_updateBinOptions()基于数据包围盒计算公共空间下的radiusCommon与六边形原点hexOriginCommon并求出 bin ID 范围binIdRangehexagon-layer.ts。它还会构造一个以数据质心为基准的专用聚合视口aggregatorViewport消除因初始视口不同带来的浮点精度差异。聚合AggregationCPU 或 GPU 聚合器对positions、colorWeights、elevationWeights三个属性执行归约归约算子即colorAggregation/elevationAggregation。缩放与过滤Scaling Filtering聚合结果经AttributeWithScale.update()结合colorScaleType、elevationScaleType与四个 percentile 参数生成颜色/高度属性及裁剪域cutoff。渲染RenderingHexagonCellLayer以六边形顶点渲染柱体颜色纹理负责查色光照材质作用于拉伸面。数据变化时的增量更新逻辑由onAttributeChange处理hexagon-layer.tspositions变化触发重分箱colorWeights/elevationWeights变化只触发对应通道的重新聚合。小结HexagonLayer 以六边形分箱 双重聚合颜色/高度为核心通过getColorWeight/getElevationWeight配合colorAggregation/elevationAggregation即可覆盖绝大多数密度、求和、均值类热力图需求遇到非内置聚合逻辑时可切换至 CPU 聚合并使用getColorValue/getElevationValue自由定义。性能敏感型应用应结合数据规模与分布选择 GPU/CPU 路径并留意 percentile 过滤与 quantile/ordinal 比例尺带来的 GPU 读回开销。进一步探索可阅读 聚合图层总览、GridLayer、ContourLayer 等兄弟图层或深入 aggregation-layers 模块源码 与 测试用例 学习底层实现。【免费下载链接】deck.glWebGL2 powered visualization framework项目地址: https://gitcode.com/GitHub_Trending/de/deck.gl创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考