Skip to content

Commit

Permalink
feat: add heatmap mark and shape (#5047)
Browse files Browse the repository at this point in the history
* feat: add heatmap mark and shape

* feat: add heatmap canvas readerer

* fix: add createCanvas to G2Context so that heatmap can use it on server-side rendering

* fix: remove createImage when creating GCanvas

* feat: add heatmap mark

* test: add test cases

* docs: add website demo of heatmap

* chore: test work

* fix: animate = false

* chore: merge v5

* test: add testcases for desity & heatmap

* docs: add document for heatmap

* feat: update gradient config

---------

Co-authored-by: yuqi.pyq <[email protected]>
  • Loading branch information
hustcc and xiaoiver authored May 25, 2023
1 parent c20cef9 commit 67d88f4
Show file tree
Hide file tree
Showing 46 changed files with 3,418 additions and 27 deletions.
2,502 changes: 2,502 additions & 0 deletions __tests__/data/heatmap.json

Large diffs are not rendered by default.

Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 6 additions & 0 deletions __tests__/integration/utils/renderSpec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Canvas } from '@antv/g';
import { createCanvas } from 'canvas';
import { G2Context, G2Spec, render } from '../../../src';
import { renderToMountedElement } from '../../utils/renderToMountedElement';
import { createNodeGCanvas } from './createNodeGCanvas';
Expand All @@ -14,6 +15,11 @@ export async function renderSpec(
const renderFunction = mounted ? renderToMountedElement : render;
const options = preprocess({ ...raw, width, height });
context.canvas = gCanvas;
context.createCanvas = () => {
// The width attribute defaults to 300, and the height attribute defaults to 150.
// @see https://stackoverflow.com/a/12019582
return createCanvas(300, 150) as unknown as HTMLCanvasElement;
};
await new Promise<Canvas>((resolve) =>
// @ts-ignore
renderFunction({ theme: 'classic', ...options }, context, resolve),
Expand Down
64 changes: 64 additions & 0 deletions __tests__/plots/static/diamond-heatmap-density.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import DataSet from '@antv/data-set';
import { G2Spec } from '../../../src';

export function DiamondHeatmapDensity(): G2Spec {
return {
type: 'view',
data: {
type: 'fetch',
value: 'data/diamond.csv',
},
scale: {
x: { nice: true, domainMin: -0.5 },
y: { nice: true, domainMin: -2000 },
color: { nice: true },
},
children: [
{
type: 'heatmap',
data: {
transform: [
{
type: 'custom',
callback: (data) => {
const dv = new DataSet.View().source(data);
// @ts-ignore
dv.transform({
type: 'kernel-smooth.density',
fields: ['carat', 'price'],
as: ['carat', 'price', 'density'],
});
return dv.rows;
},
},
],
},
encode: {
x: 'carat',
y: 'price',
color: 'density',
},
style: {
opacity: 0.3,
gradient: [
[0, 'white'],
[0.2, 'blue'],
[0.4, 'cyan'],
[0.6, 'lime'],
[0.8, 'yellow'],
[0.9, 'red'],
],
},
},
{
type: 'point',
encode: {
x: 'carat',
y: 'price',
},
},
],
};
}

DiamondHeatmapDensity.maxError = 100;
35 changes: 35 additions & 0 deletions __tests__/plots/static/heatmap-heatmap-basic.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { G2Spec } from '../../../src';

export function HeatmapHeatmapBasic(): G2Spec {
return {
type: 'view',
padding: 0,
children: [
{
type: 'image',
style: {
src: 'https://gw.alipayobjects.com/zos/rmsportal/NeUTMwKtPcPxIFNTWZOZ.png',
x: '50%',
y: '50%',
width: '100%',
height: '100%',
},
},
{
type: 'heatmap',
data: {
type: 'fetch',
value: 'data/heatmap.json',
},
encode: {
x: 'g',
y: 'l',
color: 'tmp',
},
axis: false,
},
],
};
}

HeatmapHeatmapBasic.maxError = 100;
2 changes: 2 additions & 0 deletions __tests__/plots/static/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -228,3 +228,5 @@ export { vaccinesCellScaleRelationAutoPaddingTickFilter } from './vaccines-cell-
export { marketIntervalMarimekkoAutoPaddingFlex } from './market-interval-marimekko-auto-padding-flex';
export { aaplIntervalDateEncodeX } from './aapl-interval-date-encode-x';
export { athletesRectBinLegendStyle } from './athletes-rect-bin-legend-style';
export { HeatmapHeatmapBasic } from './heatmap-heatmap-basic';
export { DiamondHeatmapDensity } from './diamond-heatmap-density';
6 changes: 6 additions & 0 deletions __tests__/unit/api/chart.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ import {
Tree,
WordCloud,
Gauge,
Density,
Heatmap,
} from '../../../src/api/mark/mark';

const TEST_OPTIONS = {
Expand Down Expand Up @@ -162,6 +164,8 @@ describe('Chart', () => {
expect(chart.tree()).toBeInstanceOf(Tree);
expect(chart.wordCloud()).toBeInstanceOf(WordCloud);
expect(chart.gauge()).toBeInstanceOf(Gauge);
expect(chart.density()).toBeInstanceOf(Density);
expect(chart.heatmap()).toBeInstanceOf(Heatmap);
expect(chart.options().children).toEqual([
{ type: 'interval' },
{ type: 'rect' },
Expand Down Expand Up @@ -190,6 +194,8 @@ describe('Chart', () => {
{ type: 'tree' },
{ type: 'wordCloud' },
{ type: 'gauge' },
{ type: 'density' },
{ type: 'heatmap' },
]);
});

Expand Down
4 changes: 4 additions & 0 deletions __tests__/unit/api/composition.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ import {
ForceGraph,
Tree,
WordCloud,
Density,
Heatmap,
} from '../../../src/api/mark/mark';

function expectToCreateMarks(node) {
Expand Down Expand Up @@ -61,6 +63,8 @@ function expectToCreateMarks(node) {
expect(node.forceGraph()).toBeInstanceOf(ForceGraph);
expect(node.tree()).toBeInstanceOf(Tree);
expect(node.wordCloud()).toBeInstanceOf(WordCloud);
expect(node.density()).toBeInstanceOf(Density);
expect(node.heatmap()).toBeInstanceOf(Heatmap);
}

function expectToCreateCompositions(node) {
Expand Down
4 changes: 4 additions & 0 deletions __tests__/unit/stdlib/index.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ import {
WordCloud as WordCloudGeometry,
Gauge,
Density as DensityGeometry,
Heatmap,
} from '../../../src/mark';
import { Category10, Category20 } from '../../../src/palette';
import {
Expand Down Expand Up @@ -111,6 +112,7 @@ import {
Path as PathShape,
HollowPath,
Density as DensityShape,
Heatmap as HeatmapShape,
Shape as CustomShape,
} from '../../../src/shape';
import { Classic, ClassicDark, Academy } from '../../../src/theme';
Expand Down Expand Up @@ -370,6 +372,7 @@ describe('stdlib', () => {
'mark.wordCloud': WordCloudGeometry,
'mark.density': DensityGeometry,
'mark.gauge': Gauge,
'mark.heatmap': Heatmap,
'palette.category10': Category10,
'palette.category20': Category20,
'scale.linear': Linear,
Expand Down Expand Up @@ -446,6 +449,7 @@ describe('stdlib', () => {
'shape.path.path': PathShape,
'shape.path.hollow': HollowPath,
'shape.density.density': DensityShape,
'shape.heatmap.heatmap': HeatmapShape,
'theme.classic': Classic,
'theme.classicDark': ClassicDark,
'theme.academy': Academy,
Expand Down
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,9 +64,11 @@
"d3-scale-chromatic": "^3.0.0",
"d3-shape": "^3.1.0",
"d3-voronoi": "^1.1.4",
"flru": "^1.0.2",
"pdfast": "^0.2.0"
},
"devDependencies": {
"@antv/data-set": "^0.11.8",
"@antv/g-plugin-rough-canvas-renderer": "^1.7.9",
"@antv/g-plugin-rough-svg-renderer": "^1.7.9",
"@antv/g-svg": "^1.8.6",
Expand Down
2 changes: 2 additions & 0 deletions site/.dumi/global.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ if (window) {
(window as any).gWebgl = require('@antv/g-webgl');
(window as any).fecha = require('fecha');
(window as any).React = require('react');
(window as any).dataSet = require('@antv/data-set');
(window as any).lodash = require('lodash');
}

if (
Expand Down
4 changes: 4 additions & 0 deletions site/docs/api/chart.zh.md
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,10 @@ chart.render();

添加 density 图形,具体见 [mark](/spec/mark/density)

### `chart.heatmap`

添加 heatmap 图形,具体见 [mark](/spec/mark/heatmap)

### `chart.shape`

添加 shape 图形,具体见 [mark](/spec/mark/shape)
Expand Down
1 change: 1 addition & 0 deletions site/docs/api/overview.zh.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ order: 1
- [chart.**treemap**](/api/chart#charttreemap) - 添加 treemap 标记到该视图。
- [chart.**boxplot**](/api/chart#chartboxplot) - 添加 boxplot 标记到该视图。
- [chart.**density**](/api/chart#density) - 添加 density 标记到该视图。
- [chart.**heatmap**](/api/chart#heatmap) - 添加 heatmap 标记到该视图。
- [chart.**shape**](/api/chart#chartshape) - 添加 shape 标记到该视图。
- [chart.**pack**](/api/chart#chartpack) - 添加 pack 标记到该视图。
- [chart.**forceGraph**](/api/chart#chartforcegraph) - 添加 forceGraph 标记到该视图。
Expand Down
4 changes: 4 additions & 0 deletions site/docs/api/view.zh.md
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,10 @@ chart.render();

添加 density 图形,具体见 [mark](/spec/mark/density)

### `chart.heatmap`

添加 heatmap 图形,具体见 [mark](/spec/mark/heatmap)

### `view.shape`

添加 shape 图形,具体见 [mark](/spec/mark/shape)
Expand Down
6 changes: 6 additions & 0 deletions site/docs/spec/mark/heatmap.en.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
title: heatmap
order: 1
---

<embed src="@/docs/spec/mark/heatmap.zh.md"></embed>
87 changes: 87 additions & 0 deletions site/docs/spec/mark/heatmap.zh.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
---
title: heatmap
order: 1
---

用于绘制密度热力图,使用的时候,需要指定数据通道 encode 中的 `x``y``color`

## 开始使用

<img alt="heatmap" src="https://mdn.alipayobjects.com/huamei_qa8qxu/afts/img/A*ze7gSYylw_QAAAAAAAAAAAAADmJ7AQ/original" width="600" />

```js
import { Chart } from '@antv/g2';

const chart = new Chart({
container: 'container',
theme: 'classic',
autoFit: true,
padding: 0,
});

chart.axis(false);

chart
.image()
.style(
'src',
'https://gw.alipayobjects.com/zos/rmsportal/NeUTMwKtPcPxIFNTWZOZ.png',
)
.style('x', '50%')
.style('y', '50%')
.style('width', '100%')
.style('height', '100%')
.tooltip(false);

chart
.heatmap()
.data({
type: 'fetch',
value: 'https://assets.antv.antgroup.com/g2/heatmap.json',
})
.encode('x', 'g')
.encode('y', 'l')
.encode('color', 'tmp')
.style('opacity', 0)
.tooltip(false);

chart.render();
```

## 选项

对于 heatmap 图形的样式配置中,主要有以下:


| 属性 | 描述 | 类型 | 默认值 |
| -------------- | ------------------------------------------------------------------------------------------------------------ | ------------------------------------------------- | ------------------------------ |
| gradient | 图形对应的渐变色配置 | `string` \| `Array<[number, string]>` | - |
| opacity | 热力图的透明度 ,如果设置,则会覆盖 `maxOpacity`, `minOpacity` 配置,范围 0 ~ 1 | `number` | `0.6` |
| maxOpacity | 热力图像素点透明度最大值,在 `opacity = 0` 时候生效,范围 0 ~ 1 | `number` | `1` |
| minOpacity | 热力图像素点透明度最小值,在 `opacity = 0` 时候生效,范围 0 ~ 1 | `number` | `0` |
| blur | 热力图的模糊因子,范围 0 ~ 1,越大图形约平滑 | `number` | `0.85` |
| useGradientOpacity | 图形的填充色 | `boolean` | `false` |
| fill | 图形的填充色 | `string` \| `Function<string>` | - |
| fillOpacity | 图形的填充透明度 | `number` \| `Function<number>` | - |
| stroke | 图形的描边 | `string` \| `Function<string>` | - |
| strokeOpacity | 描边透明度 | `number` \| `Function<number>` | - |
| lineWidth | 图形描边的宽度 | `number` \| `Function<number>` | - |
| lineDash | 描边的虚线配置,第一个值为虚线每个分段的长度,第二个值为分段间隔的距离。lineDash 设为[0, 0]的效果为没有描边。 | `[number,number]` \| `Function<[number, number]>` | - |
| shadowColor | 图形阴影颜色 | `string` \| `Function<string>` | - |
| shadowBlur | 图形阴影的高斯模糊系数 | `number` \| `Function<number>` | - |
| shadowOffsetX | 设置阴影距图形的水平距离 | `number` \| `Function<number>` | - |
| shadowOffsetY | 设置阴影距图形的垂直距离 | `number` \| `Function<number>` | - |
| cursor | 鼠标样式。同 css 的鼠标样式,默认 'default'。 | `string` \| `Function<string>` | 'default' |

关于 `gradient` 配置,来一个示例如下,也是 G2 默认内置的渐变色:

```ts
const gradient = [
[0.25, 'rgb(0,0,255)'],
[0.55, 'rgb(0,255,0)'],
[0.85, 'yellow'],
[1.0, 'rgb(255,0,0)'],
];

const gradient = '0.25:rgb(0,0,255) 0.55:rgb(0,255,0) 0.85:yellow 1.0:rgb(255,0,0)';
```
1 change: 1 addition & 0 deletions site/docs/spec/overview.zh.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ G2 是一个简洁的、渐进式的可视化语法。文档将按照下面的
- [polygon](/spec/mark/polygon) - 利用多组 (x, y) 数据点,在画布中绘制闭合的多边形,通常结合一些社区布局算法使用。
- [wordcloud](/spec/mark/wordcloud) - 绘制词云图。
- [density](/spec/mark/density) - 渲染核密度数据,多用于小提琴图。
- [heatmap](/spec/mark/heatmap) - 接受热力数据,多用于绘制热力图。

## Transform

Expand Down
Loading

0 comments on commit 67d88f4

Please sign in to comment.