Vega Voronoi 变换完全指南:基于 vega-voronoi 计算数据点单元路径

发布时间:2026/9/24 6:20:03
Vega Voronoi 变换完全指南:基于 vega-voronoi 计算数据点单元路径 数据可视化【免费下载链接】vegaA visualization grammar.项目地址https://gitcode.com/gh_mirrors/ve/vega点击查看免费下载vega-voronoi是 Vega 生态中专门负责计算 Voronoi沃罗诺伊图变换的独立数据流dataflow包。本文以该包的 README 与官方变换文档为主体结合src/Voronoi.js源码实现、test/voronoi-test.js测试用例以及 TypeScript 类型定义系统讲解 voronoi 变换的参数语义、SVG 路径输出格式、底层 Delaunay 三角剖分原理与实战用法。读完本文你将能够独立在 Vega 规范中使用voronoi变换实现鼠标悬停自动吸附最近数据点等经典交互方案。包定位与适用场景packages/vega-voronoi/README.md将本包定位为 Voronoi diagram transform for Vega dataflows即 Vega 数据流中的 Voronoi 图变换为 Vega 提供一个名为Voronoi的数据变换。Voronoi 图将平面按照一组输入点划分为若干单元cell每个单元包含距离该单元内种子点最近的区域——空间中的任意位置都可以快速判断其最近的输入点是谁。在 Vega 中最典型的应用是交互式最近点识别例如把 Voronoi 单元渲染成透明路径当鼠标悬停到任意单元上时即可自动选中距离鼠标最近的原始数据点无需逐点计算距离。docs/docs/transforms/voronoi.md明确说明A Voronoi diagram can be used to automatically select the data point closest to the mouse cursor这正是本变换的核心价值。包结构与依赖关系从仓库结构看vega-voronoi包非常精简仅包含 4 个核心文件src/Voronoi.js变换核心实现全部逻辑约 64 行test/voronoi-test.js单元测试index.js入口导出voronoipackage.json包元数据与依赖声明。根据 package.json其运行依赖仅有三项依赖作用d3-delaunay(^6.0.4)提供Delaunay.from(...).voronoi(bounds)底层几何计算vega-dataflow(^6.1.2)提供Transform基类与数据流 pulse 机制vega-util(^2.1.0)提供inherits等工具函数在 packages/vega/index.js 中主包通过import * as voronoi from vega-voronoi引入并注册该变换因此在 Vega 主包环境下可直接使用type: voronoi的变换声明无需单独加载插件。变换参数详解Voronoi 变换共接受 4 个参数其定义集中在 src/Voronoi.js 的Voronoi.Definition中。下表与官方文档 voronoi.md 保持一致并补充了源码中的默认值参数类型必填默认值说明xField是—输入数据点的 x 坐标字段yField是—输入数据点的 y 坐标字段extentArray[]否[[-1e5, -1e5], [1e5, 1e5]]Voronoi 单元的裁剪范围格式为[[x0, y0], [x1, y1]]其中 x0 为左边界、y0 为上边界、x1 为右边界、y1 为下边界sizeNumber[]否—extent的替代写法等价于把裁剪范围设置为[[0, 0], size]asString否path输出字段名保存 Voronoi 单元的 SVG path 字符串关键语义说明x、y必填两者均需指向数据中的数值字段源码中直接以字段访问器形式传入Delaunay.from(data, _.x, _.y)。extent与size互斥源码中size优先二者都未提供时才采用默认裁剪范围[-1e5, -1e5, 1e5, 1e5]即向负、正两个方向各裁剪 10,000 像素。默认范围足够大适合绝大多数屏幕坐标系。as默认写入path字段每个输入数据行会新增一个字符串字段默认名为path其值为对应 Voronoi 单元的多边形路径若某数据点无法构成有效单元则该字段置为null详见后文退化情形。用法示例基础用法官方文档 voronoi.md 给出的最小用法如下{type: voronoi, x: layout_x, y: layout_y, as: cell}该变换基于先前计算出的布局坐标layout_x、layout_y计算 Voronoi 单元路径并把结果写入新字段cell。随后即可用pathmark 引用该字段渲染单元{ type: path, from: {data: points}, transform: [ {type: voronoi, x: datum.x, y: datum.y, size: [{signal: width}, {signal: height}]} ], encode: { enter: {stroke: {value: firebrick}, fill: {value: transparent}} } }完整的交互式示例仓库中的 docs/docs/transforms/voronoi.vg.json 是一个可直接运行的交互式演示单击或拖拽添加数据点Shift 单击或 Shift 拖拽删除数据点。其核心结构展示了 voronoi 变换与信号、触发器协同工作的完整模式信号层addPoint信号监听click[!event.shiftKey]与mousemove[event.buttons !event.shiftKey]事件通过invert(xscale, x())把像素坐标反算为数据坐标remPoint信号监听path:click[event.shiftKey]等事件返回被删除的数据对象。数据层table数据集通过触发器{trigger: addPoint, insert: addPoint}与{trigger: remPoint, remove: remPoint}动态增删数据行。变换层pathmark 的transform数组中声明{type: voronoi, x: datum.x, y: datum.y, size: [{signal: width}, {signal: height}]}其中size直接绑定画布宽高信号确保单元始终覆盖整个可视区域。渲染层pointsmarksymbol 类型zindex: 1负责绘制数据点pathmark 负责绘制半透明单元边界。值得注意示例中 voronoi 变换的输入是pathmark 的datum即经过xscale/yscale映射后的屏幕坐标输出被写回path字段供当前 mark 直接使用——这是 voronoi 变换输出即路径、随用随算的典型用法。实战案例机场地图鼠标悬停加速docs/tutorials/airports/index.md第 567~596 行演示了本变换最经典的实战场景。原始机场地图需要精确悬停在细小圆点上才能查看信息体验较差。解决方案是为每个机场生成 Voronoi 单元使鼠标只要靠近某个机场就会被吸附{type: voronoi, x: x, y: y}教程明确说明Thevoronoitransform computes the enclosing cells for each airport using thexandycoordinates. The output is an SVG path string written to thepathproperty. 完整可运行版本见 docs/tutorials/airports/airports-voronoi.vg.json该文件通过hover信号监听cell:mouseover/cell:mouseout事件配合title信号实时显示hover.name ( hover.iata )实现了鼠标靠近任意机场即高亮并显示名称的交互效果。源码实现深度剖析核心流程src/Voronoi.js 的transform方法完整实现了变换逻辑可分为三步空数据短路若pulse.source为空!data || !data.length直接返回原 pulse不做任何计算。确定裁剪范围并构建图按size→extent→ 默认范围的优先级归一化边界然后调用const voronoi this.value Delaunay.from(data, _.x, _.y).voronoi(s);这里Delaunay.from(data, x, y)先用输入点构建 Delaunay 三角剖分再调用.voronoi(s)生成以s [x0, y0, x1, y1]为裁剪边界的 Voronoi 图。注意结果被缓存在this.value中这为后续增量更新提供了基础。逐点输出路径遍历每个数据行调用voronoi.cellPolygon(i)取得第 i 个点的单元多边形转换为 SVG path 字符串写入输出字段。SVG path 的生成细节多边形转 path 的逻辑在 src/Voronoi.js 的两个辅助函数中function toPathString(p) { const x p[0][0], y p[0][1]; let n p.length - 1; for (; p[n][0] x p[n][1] y; --n); return M p.slice(0, n 1).join(L) Z; } function isPoint(p) { return p.length 2 p[0][0] p[1][0] p[0][1] p[1][1]; }两个细节值得注意去重闭合点cellPolygon返回的多边形首尾顶点重合起点即终点toPathString会从尾部向前跳过与起点重合的顶点避免生成冗余线段然后以M...L...Z格式拼接。退化多边形处理若多边形只包含两个完全相同的点即单元退化成一个点例如边界上恰好重合的输入点isPoint判定为真此时不生成路径而是写入null。变换代码中对应的判断为polygon !isPoint(polygon) ? toPathString(polygon) : null。数据流语义最后一行return pulse.reflow(_.modified()).modifies(as);体现了 Vega 数据流的两个关键语义pulse.reflow(_.modified())标记所有数据行的元数据可能变化通知下游重排reflow当x/y字段或裁剪参数被修改时触发全量重算。.modifies(as)声明输出字段默认path已被修改使依赖该字段的编码encoding能够正确更新同时配合Voronoi.Definition中声明的metadata: {modifies: true}让 Vega 在优化阶段就知道该变换会改写数据字段。测试用例验证test/voronoi-test.js 使用tape编写通过vega-dataflow的Dataflow、changeset与vega-transforms的Collect构造真实数据流进行验证三个用例覆盖了点数量的边界3 个点输入(10,10)、(20,10)、(10,20)size: [30, 20]的断言out[0].path M0,0L15,0L15,15L0,15Z out[1].path M30,0L30,20L20,20L15,15L15,0Z out[2].path M0,20L0,15L15,15L20,20Z1 个点单个输入点时Voronoi 单元退化为整个裁剪矩形得到M30,0L30,20L0,20L0,0Z。2 个点两个输入点各占半幅矩形得到M0,20L0,0L15,0L15,20Z与M30,0L30,20L15,20L15,0Z。这些断言直接验证了size参数[30, 20]对应范围[0,0]到[30,20]与路径生成的正确性是理解输出格式的最直观证据。TypeScript 类型定义在 packages/vega-typings/types/spec/transform.d.ts 中VoronoiTransform接口对参数做了类型约束与源码Definition完全对应export interface VoronoiTransform { type: voronoi; x: FieldRef; y: FieldRef; size?: Vector2number | SignalRef | SignalRef; extent?: Vector2Vector2number | SignalRef | SignalRef | SignalRef; as?: string | SignalRef; }可见size是二元向量[width, height]extent是二元向量的二元向量[[x0, y0], [x1, y1]]且二者均支持SignalRef即可以绑定信号如示例中的{signal: width}。TypeScript 类型测试见 packages/vega-typings/tests/spec/valid/airports.ts它验证了 voronoi 变换在类型检查下的合法写法。使用注意事项综合源码、测试与官方文档使用 voronoi 变换时应注意以下几点x、y必须是同一批数据行的坐标字段变换基于每个输入行 一个种子点的假设输入行与输出行一一对应不改变数据行数。裁剪范围决定单元形状单元在裁剪边界处会被截断为矩形边缘形状默认范围[-1e5, 1e5]对绝大多数场景足够若坐标值超出该范围单元可能无法完整覆盖应显式指定extent或size。size优先于extent源码中_.size非空即采用[0, 0, size[0], size[1]]只有未提供size时才读取extent。输出字段可被下游直接消费默认写入path字段可由pathmark 直接引用交互式悬停示例中则利用cell:mouseover事件选择器将datum传给信号。退化点输出null无法构成有效多边形的点如重合点导致的退化单元其输出字段为null渲染层需能容忍空路径。数据流增量更新变换会缓存 Voronoi 图到this.value并通过pulse.reflow与modifies通知下游动态增删点如voronoi.vg.json中的点击添加/Shift 点击删除可高效重算。小结vega-voronoi是一个体积小巧但能力完整的 Vega 数据变换包对外只需声明x、y、extent/size、as四个参数对内则借助d3-delaunay的 Delaunay 三角剖分完成几何计算输出可直接渲染的 SVG path 字符串。无论是为散点图添加最近点吸附交互、为地图添加悬停加速还是构造任意的邻近区域划分voronoi 变换都是 Vega 数据流中不可或缺的一环。想进一步了解其在完整 Vega 规范中的位置可参考 docs/docs/transforms.md 与 docs/docs/transforms/voronoi.md若需深入交互式示例可直接运行 docs/docs/transforms/voronoi.vg.json 与 docs/tutorials/airports/airports-voronoi.vg.json。赞分享数据可视化【免费下载链接】vegaA visualization grammar.项目地址https://gitcode.com/gh_mirrors/ve/vega点击查看免费下载相关推荐Vega Voronoi Transform 完全指南用最近邻单元实现精准的鼠标拾取与空间分区Vega Voronoi Transform 完全指南用最近邻单元实现精准的鼠标拾取与空间分区 Voronoi transform 是 Vega 可视化语法中数据可视化Vega 回归变换深入指南vega-regression 包的 Regression 与 Loess 数据变换Vega 回归变换深入指南vega regression 包的 Regression 与 Loess 数据变换 vega regression 是 Vega数据可视化PythonRobotics 中的 Voronoi 路图Voronoi Road-Map路径规划基于 Dijkstra 的图搜索实现解析PythonRobotics 中的 Voronoi 路图Voronoi Road Map路径规划基于 Dijkstra 的图搜索实现解析 本指南围绕 Py示例工程机器人创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考