- 数据可视化
- 数据分析
- 前端
【免费下载链接】SandDance
Visually explore, understand, and present your data.
SandDance 是一个用于可视化探索、理解并展示数据的开源项目,而本篇文章聚焦于它在 Power BI 生态中的落地形态——以独立自定义视觉对象(Custom Visual)形式发布的SandDance 2019。本文以仓库根目录下的 powerbi.md 为骨架,结合packages/powerbi的源码、capabilities.json、pbiviz.json等配置深入展开,帮助你理解旧版 SandDance 与 SandDance 2019 的关系、视觉对象的数据加载与过滤机制,以及如何正确配置数据字段以避免 Power BI 的自动聚合,从而充分发挥该视觉对象在海量数据上的 3D 探索能力。
背景:从 SandDance 到 SandDance 2019
SandDance 早期曾以名为"SandDance"的视觉对象发布到 Power BI 的 AppSource 市场。而当前这个 GitHub 仓库所包含的是"新版" SandDance的代码,它已作为"SandDance 2019"重新发布到 AppSource。
两者的核心差异在于技术栈完全不同:
- 旧版 SandDance是此前的独立版本,其视觉对象仍在旧报告中继续工作;
- SandDance 2019是基于当前仓库
packages/powerbi目录下全新代码实现的自定义视觉对象,属于对 SandDance 的完整重写(complete reimplementation)。
从仓库中的 pbiviz.json 可以看到该视觉对象的正式注册信息:
{ "visual": { "name": "SandDance2019", "displayName": "SandDance", "guid": "SandDance201929976D117A654D0BAB8E96507442D80B", "visualClassName": "Visual", "version": "4.2.0.2", "description": "Visually explore, understand, and present your data.", "supportUrl": "https://github.com/Microsoft/SandDance/issues", "gitHubUrl": "https://github.com/microsoft/SandDance" }, "apiVersion": "5.11.0", "author": { "name": "Microsoft Research VIDA", "email": "msrvida@microsoft.com" } }其中visual.name为SandDance2019,displayName为SandDance,这解释了为何市场上显示名为 SandDance、而内部名称区分新旧版本。apiVersion指定了所依赖的 Power BI 视觉对象 API 版本(5.11.0),与 package.json 中powerbi-visuals-api: ~5.11.0的依赖保持一致。
FAQ:新旧版本的常见疑问
原文档以 FAQ 形式回答了用户在迁移与使用中最关心的问题,这里完整保留并结合源码给出补充说明。
旧报告的 SandDance 视觉对象会自动升级为 SandDance 2019 吗?
不会。它们是两个相互独立的自定义视觉对象。即便你更新了 Power BI 环境,旧报告中使用的SandDance视觉对象也不会自动替换成SandDance 2019,需要手动更换视觉对象并重新配置。
旧版 SandDance 还能用吗?
旧版SandDance已不在市场(AppSource)中显示,但任何已包含该视觉对象的现有报告仍会像往常一样继续工作。也就是说,旧视觉对象不会被强制下线,存量报告不受影响。
SandDance 2019 是否拥有前代全部功能?
SandDance 2019是构建在完全不同技术栈之上的完整重写,因此部分功能没有被重新实现,也有不少功能得到了增强。例如:
- 3D 模式更好:支持更平滑的旋转以及平移/缩放(pan/zoom);
- 选择交互模型不同:由于在视图区域中点击会执行平移/缩放/旋转操作,选择方式与旧版有所不同。
官方建议通过创建 issue 反馈缺失的功能,以便团队在后续迭代中优先排期。这与 pbiviz.json 中supportUrl指向的 issue 追踪渠道相互印证。
SandDance 2019 支持多少数据点?
截至版本 1.3.7,SandDance 2019 最多支持 500K(50 万)数据点。
需要注意的是,这个上限与视觉对象本身的分段获取(segmented fetch)机制配合生效。在 visual.ts 的update()中可以看到,视觉对象通过host.fetchMoreData()分批次拉取数据:
if (dataView.metadata.segment) { doneFetching = !this.host.fetchMoreData(); } this.app.fetchStatus(dataView.table.rows.length, !doneFetching); if (doneFetching) { this.show(dataView); } else { this.fetchMoreTimer = window.setTimeout(() => { this.app.fetchStatus(dataView.table.rows.length, false); this.show(dataView); }, Visual.fetchMoreTimeout); }当数据被分成多个 segment 时,视觉对象会先展示已到手的部分,同时以Visual.fetchMoreTimeout = 5000(毫秒)的间隔继续请求后续数据,最终把所有 segment 汇总到 Explorer 中渲染。
使用要点:防止 Power BI 自动聚合
原文档强调了一个非常关键的实战问题:
Power BI 在把数据下发给自定义视觉对象之前会对数据进行聚合(aggregate)。而 SandDance 在非聚合数据上工作效果最佳,因此请务必向下发送至少一列唯一 ID,以避免 Power BI 执行聚合。
这一建议在源码层面有清晰的体现。视觉对象的数据角色定义在 capabilities.json 中:
"dataRoles": [ { "displayName": "Values", "name": "values", "kind": "GroupingOrMeasure" } ]唯一的数据角色values同时接受分组列(Grouping)与度量值(Measure)。当你放入一列唯一的 ID 字段时,Power BI 会将其视为分组维度,从而按行原样下发明细数据;反之,如果所有字段都是可聚合的度量值,Power BI 可能会对相同维度组合进行求和、计数等聚合,导致 SandDance 拿不到原始明细,图表中相同数值的数据点会被合并,无法真实反映数据分布。
capabilities.json中的dataViewMappings还配置了默认的窗口化数据缩减策略:
"dataViewMappings": [ { "table": { "rows": { "for": { "in": "values" }, "dataReductionAlgorithm": { "window": { "count": 30000 } } } } } ]即单次下发到视觉对象的行数窗口默认为 30000 行,配合前述分段获取机制,突破单窗口限制、支撑更大规模的数据探索。
视觉对象的数据转换与选择/过滤桥接
SandDance 在浏览器中操作的是通用对象数组,而 Power BI 需要借助选择 ID(SelectionId)和筛选器(IFilter)实现与报表其他视觉对象的联动。packages/powerbi/src下的几个模块承担了这层桥接工作。
表数据转对象数组:convertTableToObjectArray
convertTableToObjectArray.ts 将 Power BI 的DataViewTable转换为 SandDance 可用的对象数组,并为每一行附加一个内部字段SandDance.constants.FieldNames.PowerBISelectionId:
newObject[SandDance.constants.FieldNames.PowerBISelectionId] = host.createSelectionIdBuilder().withTable(table, ri).createSelectionId();该函数还会对比新旧数据的行数与列键,返回different标志。在 visual.ts 的show()中,different决定走showDifferent(重新加载数据)还是showSame(仅同步状态),从而避免在报表刷新时对相同数据做无谓的重复加载。
搜索表达式转 Power BI 筛选器:convertFilter
当用户在 SandDance 中执行搜索、框选或选择操作时,SandDance 会生成"搜索表达式"(search expression)。convertFilter.ts 负责把这些表达式转换为 Power BI 的原生筛选器与选择 ID:
- 对普通字段表达式,通过
convertExpressionOperator将 SandDance 运算符(如==、!=、contains、starts、>、<等)映射为 Power BI 高级筛选运算符(Is、IsNot、Contains、StartsWith、GreaterThan等); - 对
GL_ORDINAL(WebGL 绘制序数)表达式,由于 Power BI API 无法按单行身份过滤,代码会找到对应数据点、按"值相同"的数据点构造筛选器,并将该行单独加入selectedIds完成精确选择。
最终在Visual.onSelectionChanged/onDataFilter回调中,通过host.applyJsonFilter把筛选器合并到报表的"通用筛选"对象上,实现跨视觉对象的联动:
applyFilters(filters: powerbiModels.IFilter[]) { this.host.applyJsonFilter(null, 'general', 'filter', powerbiVisualsApi.FilterAction.merge); this.host.applyJsonFilter(filters, 'general', 'filter', powerbiVisualsApi.FilterAction.merge); }这里的'general', 'filter'正是 capabilities.json 中objects.general.properties.filter所声明的筛选器挂载点。
视觉对象的状态持久化机制
Power BI 报表要求视觉对象在切换视图模式、刷新或重新打开后能恢复用户配置。SandDance 2019 通过sandDanceConfig对象实现状态的序列化与恢复。
持久化哪些状态
在 settings.ts 中定义了SandDanceConfig,对应capabilities.json中sandDanceConfig对象下的一组 JSON 字符串属性:
| 属性 | 说明 |
|---|---|
setupJSON | 相机视角、渲染器等视图设置(SandDance.types.Setup) |
insightJSON | 当前洞察配置(图表类型、编码映射等Insight) |
selectionQueryJSON | 当前选择对应的搜索表达式 |
snapshotsJSON | 用户保存的快照(Snapshot)列表 |
tooltipExclusionsJSON | 需要在 tooltip 中排除的字段列表 |
imageHolderJSON | 背景图等图像持有者状态 |
何时写入
在 visual.ts 的persist()中,视觉对象在非 View 模式(即编辑模式)下才会把上述状态通过host.persistProperties写回报表属性。写入前会用util.deepCompare与当前设置做比对,若内容未变化则跳过写入(persist skipped),避免无意义的属性更新。
何时恢复
在showDifferent/showSame中,分别通过tryGetSetup、tryGetInsight、syncSelection、tryUpdateSnapshots、syncBackgroundImage等函数从 JSON 字符串中解析并恢复对应状态。这些函数都包裹了try/catch,解析失败时静默跳过,保证旧配置或损坏 JSON 不会导致视觉对象崩溃。
主设置面板
capabilities.json中另一个对象sandDanceMainSettings暴露给用户在格式面板中直接操作:
"sandDanceMainSettings": { "displayName": "SandDance", "properties": { "showchrome": { "displayName": "Show chrome", "type": { "bool": true } }, "darktheme": { "displayName": "Dark theme", "type": { "bool": true } } } }对应的默认值在 settings.ts 的SandDanceMainSettings中声明:showchrome = true、darktheme = false、showdebug = false。在show()中,视觉对象会根据这两个开关调用app.setChromeless(!sandDanceMainSettings.showchrome)与app.changeTheme(sandDanceMainSettings.darktheme),实时切换界面 chrome 与深色主题(主题色板来自themePalettes['dark-theme'],见 app.ts)。
在 Power BI 中使用 SandDance 2019
获取视觉对象
SandDance 2019 已作为自定义视觉对象发布到 AppSource(Power BI 背后的市场),你可以在 Power BI 的"从市场获取视觉对象"中搜索并导入。
数据字段配置建议
- 至少放入一列唯一 ID(如行号、订单号、主键等),作为分组维度下发,避免 Power BI 自动聚合;
- 其余维度字段用于图表的坐标轴、颜色、大小等编码;度量字段(数值)用于位置或大小的定量映射;
- 合理控制下发的字段数量——视觉对象会把
DataViewTable逐行转换为对象数组(见convertTableToObjectArray),字段越多,单行对象与内存开销越大。
交互与联动
- 在视图区域点击会触发平移/缩放/旋转,右键点击弹出 Power BI 上下文菜单(见 app.ts 的
onCubeClick处理,右键按钮常量为RIGHT_MOUSE_BUTTON = 2); - 框选(lasso)被禁用(
disableLasso: true),搜索、选择操作会转换为 Power BI 筛选器并联动报表中的其他视觉对象; - 相机在旋转/缩放结束后会稳定并保存(app.ts 的相机监听机制),这样用户刷新或切换页面后视角得以保留。
开发与构建
如果你是开发者,希望在本地构建该视觉对象,packages/powerbi的 package.json 提供了完整脚本:
npm start:等价于pbiviz start,启动本地开发服务器并热加载调试;npm run build:08:等价于pbiviz package,打包生成.pbiviz文件(打包前会通过prebuild:08执行 scripts/version.js,把pbiviz.json中的版本号写入src/version.ts);npm run deploy:执行 scripts/deploy.js,将dist/下生成的SandDance201929976D117A654D0BAB8E96507442D80B.<version>.pbiviz拷贝到docs/dist/powerbi/v4/目录归档。
构建产物遵循 Power BI 视觉对象 CLI 的标准流程:由pbiviz.json指定capabilities.json(能力声明)与style/visual.less(样式),入口为src/visual.ts中导出的Visual类(visualClassName: "Visual")。
小结
SandDance 2019 是 SandDance 在 Power BI 平台上的全新实现:它以独立的自定义视觉对象形态存在,与旧版 SandDance 互不干扰;在数据层面通过"唯一 ID 防聚合 + 分段获取"的策略支撑最高 500K 数据点的明细探索;在联动层面通过搜索表达式到 Power BI 筛选器/选择 ID 的双向桥接,与报表其他视觉对象深度集成;在状态层面则将相机、洞察、快照等完整序列化进报表属性,实现所见即所得的状态恢复。对于希望把 SandDance 强大的 3D 可视化探索能力接入 Power BI 报表的开发者与数据分析师,理解上述机制将帮助你避开聚合陷阱,并最大化该视觉对象的实战价值。
- 数据可视化
- 数据分析
- 前端
【免费下载链接】SandDance
Visually explore, understand, and present your data.
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考