@hpcc-js/chart 3.7.2 → 3.7.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +43 -43
- package/README.md +93 -93
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/index.umd.cjs +1 -1
- package/dist/index.umd.cjs.map +1 -1
- package/package.json +6 -6
- package/src/Area.md +176 -176
- package/src/Area.ts +12 -12
- package/src/Axis.css +35 -35
- package/src/Axis.ts +781 -781
- package/src/Bar.md +90 -90
- package/src/Bar.ts +9 -9
- package/src/Bubble.css +16 -16
- package/src/Bubble.md +69 -69
- package/src/Bubble.ts +196 -196
- package/src/BubbleXY.ts +14 -14
- package/src/Bullet.css +59 -59
- package/src/Bullet.md +104 -104
- package/src/Bullet.ts +176 -176
- package/src/Column.css +44 -44
- package/src/Column.md +90 -90
- package/src/Column.ts +684 -684
- package/src/Contour.md +88 -88
- package/src/Contour.ts +97 -97
- package/src/D3Cloud.ts +403 -403
- package/src/Gantt.md +119 -119
- package/src/Gantt.ts +14 -14
- package/src/Gauge.md +148 -148
- package/src/Gauge.ts +368 -368
- package/src/HalfPie.md +62 -62
- package/src/HalfPie.ts +26 -26
- package/src/Heat.md +42 -42
- package/src/Heat.ts +283 -283
- package/src/HexBin.css +8 -8
- package/src/HexBin.md +88 -88
- package/src/HexBin.ts +144 -144
- package/src/Line.css +4 -4
- package/src/Line.md +170 -170
- package/src/Line.ts +14 -14
- package/src/Pie.css +50 -50
- package/src/Pie.md +88 -88
- package/src/Pie.ts +546 -546
- package/src/QuarterPie.md +61 -61
- package/src/QuarterPie.ts +35 -35
- package/src/QuartileCandlestick.md +129 -129
- package/src/QuartileCandlestick.ts +349 -349
- package/src/Radar.css +14 -14
- package/src/Radar.md +104 -104
- package/src/Radar.ts +336 -336
- package/src/RadialBar.css +25 -25
- package/src/RadialBar.md +91 -91
- package/src/RadialBar.ts +217 -217
- package/src/Scatter.css +42 -42
- package/src/Scatter.md +163 -163
- package/src/Scatter.ts +412 -412
- package/src/StatChart.md +117 -117
- package/src/StatChart.ts +261 -261
- package/src/Step.md +163 -163
- package/src/Step.ts +12 -12
- package/src/Summary.css +55 -55
- package/src/Summary.md +219 -219
- package/src/Summary.ts +322 -322
- package/src/SummaryC.md +154 -154
- package/src/SummaryC.ts +240 -240
- package/src/WordCloud.css +2 -2
- package/src/WordCloud.md +144 -144
- package/src/WordCloud.ts +268 -268
- package/src/XYAxis.css +40 -40
- package/src/XYAxis.md +149 -149
- package/src/XYAxis.ts +809 -809
- package/src/__package__.ts +3 -3
- package/src/__tests__/heat.ts +71 -71
- package/src/__tests__/index.ts +3 -3
- package/src/__tests__/pie.ts +20 -20
- package/src/__tests__/stat.ts +16 -16
- package/src/__tests__/test3.ts +68 -68
- package/src/index.ts +28 -28
- package/src/test.ts +70 -70
- package/src/timeFormats.ts +26 -26
package/src/Heat.ts
CHANGED
|
@@ -1,283 +1,283 @@
|
|
|
1
|
-
import { max, Palette } from "@hpcc-js/common";
|
|
2
|
-
import { XYAxis } from "./XYAxis.ts";
|
|
3
|
-
|
|
4
|
-
export class Heat extends XYAxis {
|
|
5
|
-
|
|
6
|
-
protected _domForeignObject;
|
|
7
|
-
protected _domCanvas;
|
|
8
|
-
protected _heat;
|
|
9
|
-
|
|
10
|
-
constructor() {
|
|
11
|
-
super();
|
|
12
|
-
this
|
|
13
|
-
.xAxisGuideLines_default(true)
|
|
14
|
-
.yAxisGuideLines_default(true)
|
|
15
|
-
;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
radius(r: number): this {
|
|
19
|
-
this.radiusX(r);
|
|
20
|
-
this.radiusY(r);
|
|
21
|
-
return this;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
layerEnter(host: XYAxis, element, duration: number = 250) {
|
|
25
|
-
super.layerEnter(host, element, duration);
|
|
26
|
-
this._domForeignObject = this.svg.insert("foreignObject", `#${this.id() + "_clippath"}`);
|
|
27
|
-
this._domCanvas = this._domForeignObject.append("xhtml:body")
|
|
28
|
-
.style("margin", "0px")
|
|
29
|
-
.style("padding", "0px")
|
|
30
|
-
.style("background-color", "transparent")
|
|
31
|
-
.append("canvas")
|
|
32
|
-
;
|
|
33
|
-
this._heat = simpleheat(this._domCanvas.node());
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
layerUpdate(host: XYAxis, element, duration: number = 250) {
|
|
37
|
-
super.layerUpdate(host, element);
|
|
38
|
-
|
|
39
|
-
this._palette = this._palette.switch(this.paletteID());
|
|
40
|
-
|
|
41
|
-
let width = this.width() - this.margin.left - this.margin.right;
|
|
42
|
-
if (width < 0) width = 0;
|
|
43
|
-
let height = this.height() - this.margin.top - this.margin.bottom;
|
|
44
|
-
if (height < 0) height = 0;
|
|
45
|
-
|
|
46
|
-
this._domForeignObject
|
|
47
|
-
.attr("x", this.margin.left)
|
|
48
|
-
.attr("y", this.margin.top)
|
|
49
|
-
.attr("width", width)
|
|
50
|
-
.attr("height", height)
|
|
51
|
-
;
|
|
52
|
-
this._domCanvas
|
|
53
|
-
.attr("width", width)
|
|
54
|
-
.attr("height", height)
|
|
55
|
-
;
|
|
56
|
-
|
|
57
|
-
const data = host.orientation() === "horizontal" ?
|
|
58
|
-
this.data().map(r => [host.dataPos(r[0]), host.valuePos(r[1]), r[2]]) :
|
|
59
|
-
this.data().map(r => [host.valuePos(r[1]), host.dataPos(r[0]), r[2]])
|
|
60
|
-
;
|
|
61
|
-
|
|
62
|
-
const maxWeight = this.maxWeight_exists() ? this.maxWeight() : max(data, r => r[2]);
|
|
63
|
-
|
|
64
|
-
if (this.paletteID() !== "default") {
|
|
65
|
-
const gradient = {};
|
|
66
|
-
const count = 8;
|
|
67
|
-
const reverse = this.reversePalette();
|
|
68
|
-
for (let i = 0; i < count; ++i) {
|
|
69
|
-
gradient[i / count] = this._palette((reverse ? count - i : i) / count, 0, 1);
|
|
70
|
-
}
|
|
71
|
-
this._heat.gradient(gradient);
|
|
72
|
-
} else {
|
|
73
|
-
this._heat.gradient(this._heat.defaultGradient);
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
this._heat.resize();
|
|
77
|
-
|
|
78
|
-
const radiusX = this.radiusAsPercent() ? this.radiusX() * width / 100 : this.radiusX();
|
|
79
|
-
const radiusY = this.radiusAsPercent() ? this.radiusY() * height / 100 : this.radiusY();
|
|
80
|
-
|
|
81
|
-
this._heat
|
|
82
|
-
.clear()
|
|
83
|
-
.radius(radiusX, radiusY, this.blur())
|
|
84
|
-
.max(maxWeight)
|
|
85
|
-
.data(data)
|
|
86
|
-
.draw(this.minOpacity())
|
|
87
|
-
;
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
Heat.prototype._class += " chart_Heat";
|
|
92
|
-
Heat.prototype._palette = Palette.rainbow("default");
|
|
93
|
-
|
|
94
|
-
export interface Heat {
|
|
95
|
-
paletteID(): string;
|
|
96
|
-
paletteID(_: string): this;
|
|
97
|
-
useClonedPalette(): boolean;
|
|
98
|
-
useClonedPalette(_: boolean): this;
|
|
99
|
-
reversePalette(): boolean;
|
|
100
|
-
reversePalette(_: boolean): this;
|
|
101
|
-
|
|
102
|
-
radiusX(): number;
|
|
103
|
-
radiusX(_: number): this;
|
|
104
|
-
radiusY(): number;
|
|
105
|
-
radiusY(_: number): this;
|
|
106
|
-
radiusAsPercent(): boolean;
|
|
107
|
-
radiusAsPercent(_: boolean): this;
|
|
108
|
-
blur(): number;
|
|
109
|
-
blur(_: number): this;
|
|
110
|
-
|
|
111
|
-
maxWeight(): number;
|
|
112
|
-
maxWeight(_: number): this;
|
|
113
|
-
maxWeight_exists(): boolean;
|
|
114
|
-
|
|
115
|
-
minOpacity(): number;
|
|
116
|
-
minOpacity(_: number): this;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
Heat.prototype.publish("paletteID", "default", "set", "Color palette for this widget", Heat.prototype._palette.switch(), { tags: ["Basic"] });
|
|
120
|
-
Heat.prototype.publish("useClonedPalette", false, "boolean", "Enable or disable using a cloned palette", null, { tags: ["Intermediate", "Shared"] });
|
|
121
|
-
Heat.prototype.publish("reversePalette", false, "boolean", "Reverse Palette Colors", null, { disable: w => w.paletteID() === "default" });
|
|
122
|
-
|
|
123
|
-
Heat.prototype.publish("radiusX", 25, "number", "Point X radius (25 by default)");
|
|
124
|
-
Heat.prototype.publish("radiusY", 25, "number", "Point Y radius (25 by default)");
|
|
125
|
-
Heat.prototype.publish("radiusAsPercent", false, "boolean", "Calculate RadiusX + RadiusY as % of size");
|
|
126
|
-
Heat.prototype.publish("blur", 15, "number", "Point blur radius (15 by default)");
|
|
127
|
-
|
|
128
|
-
Heat.prototype.publish("maxWeight", undefined, "number", "Clamp max weight to value (optional), omitting uses max data point", undefined, { optional: true });
|
|
129
|
-
Heat.prototype.publish("minOpacity", 0.05, "number", "Minimum point opacity (0.05 by default)");
|
|
130
|
-
|
|
131
|
-
// The following code is a modified version of
|
|
132
|
-
// - https://github.com/mourner/simpleheat
|
|
133
|
-
// - Licensed under BSD-2-Clause - https://github.com/mourner/simpleheat/blob/gh-pages/LICENSE
|
|
134
|
-
// Changes:
|
|
135
|
-
// * Fixed TS syntax issues
|
|
136
|
-
// * Added support for eliptical shapes instead of circles
|
|
137
|
-
|
|
138
|
-
function simpleheat(canvas): void {
|
|
139
|
-
if (!(this instanceof simpleheat)) return new simpleheat(canvas);
|
|
140
|
-
|
|
141
|
-
this._canvas = canvas = typeof canvas === "string" ? document.getElementById(canvas) : canvas;
|
|
142
|
-
|
|
143
|
-
this._ctx = canvas.getContext("2d");
|
|
144
|
-
this._width = canvas.width;
|
|
145
|
-
this._height = canvas.height;
|
|
146
|
-
|
|
147
|
-
this._max = 1;
|
|
148
|
-
this._data = [];
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
simpleheat.prototype = {
|
|
152
|
-
|
|
153
|
-
defaultRadius: 25,
|
|
154
|
-
|
|
155
|
-
defaultGradient: {
|
|
156
|
-
0.4: "blue",
|
|
157
|
-
0.6: "cyan",
|
|
158
|
-
0.7: "lime",
|
|
159
|
-
0.8: "yellow",
|
|
160
|
-
1.0: "red"
|
|
161
|
-
},
|
|
162
|
-
|
|
163
|
-
data: function (data) {
|
|
164
|
-
this._data = data;
|
|
165
|
-
return this;
|
|
166
|
-
},
|
|
167
|
-
|
|
168
|
-
max: function (max) {
|
|
169
|
-
this._max = max;
|
|
170
|
-
return this;
|
|
171
|
-
},
|
|
172
|
-
|
|
173
|
-
add: function (point) {
|
|
174
|
-
this._data.push(point);
|
|
175
|
-
return this;
|
|
176
|
-
},
|
|
177
|
-
|
|
178
|
-
clear: function () {
|
|
179
|
-
this._data = [];
|
|
180
|
-
return this;
|
|
181
|
-
},
|
|
182
|
-
|
|
183
|
-
radius: function (rX, rY, blur) {
|
|
184
|
-
blur = blur === undefined ? 15 : blur;
|
|
185
|
-
|
|
186
|
-
// create a grayscale blurred ellipse image that we'll use for drawing points
|
|
187
|
-
const ellipse = this._ellipse = this._createCanvas();
|
|
188
|
-
const ctx = ellipse.getContext("2d");
|
|
189
|
-
const rX2 = this._r = rX + blur;
|
|
190
|
-
const rY2 = this._r = rY + blur;
|
|
191
|
-
|
|
192
|
-
ellipse.width = rX2 * 2;
|
|
193
|
-
ellipse.height = rY2 * 2;
|
|
194
|
-
|
|
195
|
-
ctx.shadowOffsetX = ctx.shadowOffsetY = rX2 * 2;
|
|
196
|
-
ctx.shadowOffsetY = ctx.shadowOffsetY = rY2 * 2;
|
|
197
|
-
ctx.shadowBlur = blur;
|
|
198
|
-
ctx.shadowColor = "black";
|
|
199
|
-
|
|
200
|
-
ctx.beginPath();
|
|
201
|
-
ctx.ellipse(-rX2, -rY2, rX, rY, 0, 0, Math.PI * 2, true);
|
|
202
|
-
ctx.closePath();
|
|
203
|
-
ctx.fill();
|
|
204
|
-
|
|
205
|
-
return this;
|
|
206
|
-
},
|
|
207
|
-
|
|
208
|
-
resize: function () {
|
|
209
|
-
this._width = this._canvas.width;
|
|
210
|
-
this._height = this._canvas.height;
|
|
211
|
-
},
|
|
212
|
-
|
|
213
|
-
gradient: function (grad) {
|
|
214
|
-
// create a 256x1 gradient that we'll use to turn a grayscale heatmap into a colored one
|
|
215
|
-
const canvas = this._createCanvas(),
|
|
216
|
-
ctx = canvas.getContext("2d"),
|
|
217
|
-
gradient = ctx.createLinearGradient(0, 0, 0, 256);
|
|
218
|
-
|
|
219
|
-
canvas.width = 1;
|
|
220
|
-
canvas.height = 256;
|
|
221
|
-
|
|
222
|
-
for (const i in grad) {
|
|
223
|
-
gradient.addColorStop(+i, grad[i]);
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
ctx.fillStyle = gradient;
|
|
227
|
-
ctx.fillRect(0, 0, 1, 256);
|
|
228
|
-
|
|
229
|
-
this._grad = ctx.getImageData(0, 0, 1, 256).data;
|
|
230
|
-
|
|
231
|
-
return this;
|
|
232
|
-
},
|
|
233
|
-
|
|
234
|
-
draw: function (minOpacity) {
|
|
235
|
-
if (!this._ellipse) this.radius(this.defaultRadius, this.defaultRadius);
|
|
236
|
-
if (!this._grad) this.gradient(this.defaultGradient);
|
|
237
|
-
|
|
238
|
-
const ctx = this._ctx;
|
|
239
|
-
|
|
240
|
-
ctx.clearRect(0, 0, this._width, this._height);
|
|
241
|
-
|
|
242
|
-
// draw a grayscale heatmap by putting a blurred ellipse at each data point
|
|
243
|
-
for (let i = 0, len = this._data.length, p; i < len; i++) {
|
|
244
|
-
p = this._data[i];
|
|
245
|
-
if (p[2] < 0) {
|
|
246
|
-
p[2] = 0;
|
|
247
|
-
} else if (p[2] > this._max) {
|
|
248
|
-
p[2] = this._max;
|
|
249
|
-
}
|
|
250
|
-
ctx.globalAlpha = Math.max(p[2] / this._max, minOpacity === undefined ? 0.05 : minOpacity);
|
|
251
|
-
ctx.drawImage(this._ellipse, p[0] - this._r, p[1] - this._r);
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
// colorize the heatmap, using opacity value of each pixel to get the right color from our gradient
|
|
255
|
-
const colored = ctx.getImageData(0, 0, this._width, this._height);
|
|
256
|
-
this._colorize(colored.data, this._grad);
|
|
257
|
-
ctx.putImageData(colored, 0, 0);
|
|
258
|
-
|
|
259
|
-
return this;
|
|
260
|
-
},
|
|
261
|
-
|
|
262
|
-
_colorize: function (pixels, gradient) {
|
|
263
|
-
for (let i = 0, len = pixels.length, j; i < len; i += 4) {
|
|
264
|
-
j = pixels[i + 3] * 4; // get gradient color from opacity value
|
|
265
|
-
|
|
266
|
-
if (j) {
|
|
267
|
-
pixels[i] = gradient[j];
|
|
268
|
-
pixels[i + 1] = gradient[j + 1];
|
|
269
|
-
pixels[i + 2] = gradient[j + 2];
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
},
|
|
273
|
-
|
|
274
|
-
_createCanvas: function () {
|
|
275
|
-
if (typeof document !== "undefined") {
|
|
276
|
-
return document.createElement("canvas");
|
|
277
|
-
} else {
|
|
278
|
-
// create a new canvas instance in node.js
|
|
279
|
-
// the canvas class needs to have a default constructor without any parameter
|
|
280
|
-
return new this._canvas.constructor();
|
|
281
|
-
}
|
|
282
|
-
}
|
|
283
|
-
};
|
|
1
|
+
import { max, Palette } from "@hpcc-js/common";
|
|
2
|
+
import { XYAxis } from "./XYAxis.ts";
|
|
3
|
+
|
|
4
|
+
export class Heat extends XYAxis {
|
|
5
|
+
|
|
6
|
+
protected _domForeignObject;
|
|
7
|
+
protected _domCanvas;
|
|
8
|
+
protected _heat;
|
|
9
|
+
|
|
10
|
+
constructor() {
|
|
11
|
+
super();
|
|
12
|
+
this
|
|
13
|
+
.xAxisGuideLines_default(true)
|
|
14
|
+
.yAxisGuideLines_default(true)
|
|
15
|
+
;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
radius(r: number): this {
|
|
19
|
+
this.radiusX(r);
|
|
20
|
+
this.radiusY(r);
|
|
21
|
+
return this;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
layerEnter(host: XYAxis, element, duration: number = 250) {
|
|
25
|
+
super.layerEnter(host, element, duration);
|
|
26
|
+
this._domForeignObject = this.svg.insert("foreignObject", `#${this.id() + "_clippath"}`);
|
|
27
|
+
this._domCanvas = this._domForeignObject.append("xhtml:body")
|
|
28
|
+
.style("margin", "0px")
|
|
29
|
+
.style("padding", "0px")
|
|
30
|
+
.style("background-color", "transparent")
|
|
31
|
+
.append("canvas")
|
|
32
|
+
;
|
|
33
|
+
this._heat = simpleheat(this._domCanvas.node());
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
layerUpdate(host: XYAxis, element, duration: number = 250) {
|
|
37
|
+
super.layerUpdate(host, element);
|
|
38
|
+
|
|
39
|
+
this._palette = this._palette.switch(this.paletteID());
|
|
40
|
+
|
|
41
|
+
let width = this.width() - this.margin.left - this.margin.right;
|
|
42
|
+
if (width < 0) width = 0;
|
|
43
|
+
let height = this.height() - this.margin.top - this.margin.bottom;
|
|
44
|
+
if (height < 0) height = 0;
|
|
45
|
+
|
|
46
|
+
this._domForeignObject
|
|
47
|
+
.attr("x", this.margin.left)
|
|
48
|
+
.attr("y", this.margin.top)
|
|
49
|
+
.attr("width", width)
|
|
50
|
+
.attr("height", height)
|
|
51
|
+
;
|
|
52
|
+
this._domCanvas
|
|
53
|
+
.attr("width", width)
|
|
54
|
+
.attr("height", height)
|
|
55
|
+
;
|
|
56
|
+
|
|
57
|
+
const data = host.orientation() === "horizontal" ?
|
|
58
|
+
this.data().map(r => [host.dataPos(r[0]), host.valuePos(r[1]), r[2]]) :
|
|
59
|
+
this.data().map(r => [host.valuePos(r[1]), host.dataPos(r[0]), r[2]])
|
|
60
|
+
;
|
|
61
|
+
|
|
62
|
+
const maxWeight = this.maxWeight_exists() ? this.maxWeight() : max(data, r => r[2]);
|
|
63
|
+
|
|
64
|
+
if (this.paletteID() !== "default") {
|
|
65
|
+
const gradient = {};
|
|
66
|
+
const count = 8;
|
|
67
|
+
const reverse = this.reversePalette();
|
|
68
|
+
for (let i = 0; i < count; ++i) {
|
|
69
|
+
gradient[i / count] = this._palette((reverse ? count - i : i) / count, 0, 1);
|
|
70
|
+
}
|
|
71
|
+
this._heat.gradient(gradient);
|
|
72
|
+
} else {
|
|
73
|
+
this._heat.gradient(this._heat.defaultGradient);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
this._heat.resize();
|
|
77
|
+
|
|
78
|
+
const radiusX = this.radiusAsPercent() ? this.radiusX() * width / 100 : this.radiusX();
|
|
79
|
+
const radiusY = this.radiusAsPercent() ? this.radiusY() * height / 100 : this.radiusY();
|
|
80
|
+
|
|
81
|
+
this._heat
|
|
82
|
+
.clear()
|
|
83
|
+
.radius(radiusX, radiusY, this.blur())
|
|
84
|
+
.max(maxWeight)
|
|
85
|
+
.data(data)
|
|
86
|
+
.draw(this.minOpacity())
|
|
87
|
+
;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
Heat.prototype._class += " chart_Heat";
|
|
92
|
+
Heat.prototype._palette = Palette.rainbow("default");
|
|
93
|
+
|
|
94
|
+
export interface Heat {
|
|
95
|
+
paletteID(): string;
|
|
96
|
+
paletteID(_: string): this;
|
|
97
|
+
useClonedPalette(): boolean;
|
|
98
|
+
useClonedPalette(_: boolean): this;
|
|
99
|
+
reversePalette(): boolean;
|
|
100
|
+
reversePalette(_: boolean): this;
|
|
101
|
+
|
|
102
|
+
radiusX(): number;
|
|
103
|
+
radiusX(_: number): this;
|
|
104
|
+
radiusY(): number;
|
|
105
|
+
radiusY(_: number): this;
|
|
106
|
+
radiusAsPercent(): boolean;
|
|
107
|
+
radiusAsPercent(_: boolean): this;
|
|
108
|
+
blur(): number;
|
|
109
|
+
blur(_: number): this;
|
|
110
|
+
|
|
111
|
+
maxWeight(): number;
|
|
112
|
+
maxWeight(_: number): this;
|
|
113
|
+
maxWeight_exists(): boolean;
|
|
114
|
+
|
|
115
|
+
minOpacity(): number;
|
|
116
|
+
minOpacity(_: number): this;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
Heat.prototype.publish("paletteID", "default", "set", "Color palette for this widget", Heat.prototype._palette.switch(), { tags: ["Basic"] });
|
|
120
|
+
Heat.prototype.publish("useClonedPalette", false, "boolean", "Enable or disable using a cloned palette", null, { tags: ["Intermediate", "Shared"] });
|
|
121
|
+
Heat.prototype.publish("reversePalette", false, "boolean", "Reverse Palette Colors", null, { disable: w => w.paletteID() === "default" });
|
|
122
|
+
|
|
123
|
+
Heat.prototype.publish("radiusX", 25, "number", "Point X radius (25 by default)");
|
|
124
|
+
Heat.prototype.publish("radiusY", 25, "number", "Point Y radius (25 by default)");
|
|
125
|
+
Heat.prototype.publish("radiusAsPercent", false, "boolean", "Calculate RadiusX + RadiusY as % of size");
|
|
126
|
+
Heat.prototype.publish("blur", 15, "number", "Point blur radius (15 by default)");
|
|
127
|
+
|
|
128
|
+
Heat.prototype.publish("maxWeight", undefined, "number", "Clamp max weight to value (optional), omitting uses max data point", undefined, { optional: true });
|
|
129
|
+
Heat.prototype.publish("minOpacity", 0.05, "number", "Minimum point opacity (0.05 by default)");
|
|
130
|
+
|
|
131
|
+
// The following code is a modified version of
|
|
132
|
+
// - https://github.com/mourner/simpleheat
|
|
133
|
+
// - Licensed under BSD-2-Clause - https://github.com/mourner/simpleheat/blob/gh-pages/LICENSE
|
|
134
|
+
// Changes:
|
|
135
|
+
// * Fixed TS syntax issues
|
|
136
|
+
// * Added support for eliptical shapes instead of circles
|
|
137
|
+
|
|
138
|
+
function simpleheat(canvas): void {
|
|
139
|
+
if (!(this instanceof simpleheat)) return new simpleheat(canvas);
|
|
140
|
+
|
|
141
|
+
this._canvas = canvas = typeof canvas === "string" ? document.getElementById(canvas) : canvas;
|
|
142
|
+
|
|
143
|
+
this._ctx = canvas.getContext("2d");
|
|
144
|
+
this._width = canvas.width;
|
|
145
|
+
this._height = canvas.height;
|
|
146
|
+
|
|
147
|
+
this._max = 1;
|
|
148
|
+
this._data = [];
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
simpleheat.prototype = {
|
|
152
|
+
|
|
153
|
+
defaultRadius: 25,
|
|
154
|
+
|
|
155
|
+
defaultGradient: {
|
|
156
|
+
0.4: "blue",
|
|
157
|
+
0.6: "cyan",
|
|
158
|
+
0.7: "lime",
|
|
159
|
+
0.8: "yellow",
|
|
160
|
+
1.0: "red"
|
|
161
|
+
},
|
|
162
|
+
|
|
163
|
+
data: function (data) {
|
|
164
|
+
this._data = data;
|
|
165
|
+
return this;
|
|
166
|
+
},
|
|
167
|
+
|
|
168
|
+
max: function (max) {
|
|
169
|
+
this._max = max;
|
|
170
|
+
return this;
|
|
171
|
+
},
|
|
172
|
+
|
|
173
|
+
add: function (point) {
|
|
174
|
+
this._data.push(point);
|
|
175
|
+
return this;
|
|
176
|
+
},
|
|
177
|
+
|
|
178
|
+
clear: function () {
|
|
179
|
+
this._data = [];
|
|
180
|
+
return this;
|
|
181
|
+
},
|
|
182
|
+
|
|
183
|
+
radius: function (rX, rY, blur) {
|
|
184
|
+
blur = blur === undefined ? 15 : blur;
|
|
185
|
+
|
|
186
|
+
// create a grayscale blurred ellipse image that we'll use for drawing points
|
|
187
|
+
const ellipse = this._ellipse = this._createCanvas();
|
|
188
|
+
const ctx = ellipse.getContext("2d");
|
|
189
|
+
const rX2 = this._r = rX + blur;
|
|
190
|
+
const rY2 = this._r = rY + blur;
|
|
191
|
+
|
|
192
|
+
ellipse.width = rX2 * 2;
|
|
193
|
+
ellipse.height = rY2 * 2;
|
|
194
|
+
|
|
195
|
+
ctx.shadowOffsetX = ctx.shadowOffsetY = rX2 * 2;
|
|
196
|
+
ctx.shadowOffsetY = ctx.shadowOffsetY = rY2 * 2;
|
|
197
|
+
ctx.shadowBlur = blur;
|
|
198
|
+
ctx.shadowColor = "black";
|
|
199
|
+
|
|
200
|
+
ctx.beginPath();
|
|
201
|
+
ctx.ellipse(-rX2, -rY2, rX, rY, 0, 0, Math.PI * 2, true);
|
|
202
|
+
ctx.closePath();
|
|
203
|
+
ctx.fill();
|
|
204
|
+
|
|
205
|
+
return this;
|
|
206
|
+
},
|
|
207
|
+
|
|
208
|
+
resize: function () {
|
|
209
|
+
this._width = this._canvas.width;
|
|
210
|
+
this._height = this._canvas.height;
|
|
211
|
+
},
|
|
212
|
+
|
|
213
|
+
gradient: function (grad) {
|
|
214
|
+
// create a 256x1 gradient that we'll use to turn a grayscale heatmap into a colored one
|
|
215
|
+
const canvas = this._createCanvas(),
|
|
216
|
+
ctx = canvas.getContext("2d"),
|
|
217
|
+
gradient = ctx.createLinearGradient(0, 0, 0, 256);
|
|
218
|
+
|
|
219
|
+
canvas.width = 1;
|
|
220
|
+
canvas.height = 256;
|
|
221
|
+
|
|
222
|
+
for (const i in grad) {
|
|
223
|
+
gradient.addColorStop(+i, grad[i]);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
ctx.fillStyle = gradient;
|
|
227
|
+
ctx.fillRect(0, 0, 1, 256);
|
|
228
|
+
|
|
229
|
+
this._grad = ctx.getImageData(0, 0, 1, 256).data;
|
|
230
|
+
|
|
231
|
+
return this;
|
|
232
|
+
},
|
|
233
|
+
|
|
234
|
+
draw: function (minOpacity) {
|
|
235
|
+
if (!this._ellipse) this.radius(this.defaultRadius, this.defaultRadius);
|
|
236
|
+
if (!this._grad) this.gradient(this.defaultGradient);
|
|
237
|
+
|
|
238
|
+
const ctx = this._ctx;
|
|
239
|
+
|
|
240
|
+
ctx.clearRect(0, 0, this._width, this._height);
|
|
241
|
+
|
|
242
|
+
// draw a grayscale heatmap by putting a blurred ellipse at each data point
|
|
243
|
+
for (let i = 0, len = this._data.length, p; i < len; i++) {
|
|
244
|
+
p = this._data[i];
|
|
245
|
+
if (p[2] < 0) {
|
|
246
|
+
p[2] = 0;
|
|
247
|
+
} else if (p[2] > this._max) {
|
|
248
|
+
p[2] = this._max;
|
|
249
|
+
}
|
|
250
|
+
ctx.globalAlpha = Math.max(p[2] / this._max, minOpacity === undefined ? 0.05 : minOpacity);
|
|
251
|
+
ctx.drawImage(this._ellipse, p[0] - this._r, p[1] - this._r);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// colorize the heatmap, using opacity value of each pixel to get the right color from our gradient
|
|
255
|
+
const colored = ctx.getImageData(0, 0, this._width, this._height);
|
|
256
|
+
this._colorize(colored.data, this._grad);
|
|
257
|
+
ctx.putImageData(colored, 0, 0);
|
|
258
|
+
|
|
259
|
+
return this;
|
|
260
|
+
},
|
|
261
|
+
|
|
262
|
+
_colorize: function (pixels, gradient) {
|
|
263
|
+
for (let i = 0, len = pixels.length, j; i < len; i += 4) {
|
|
264
|
+
j = pixels[i + 3] * 4; // get gradient color from opacity value
|
|
265
|
+
|
|
266
|
+
if (j) {
|
|
267
|
+
pixels[i] = gradient[j];
|
|
268
|
+
pixels[i + 1] = gradient[j + 1];
|
|
269
|
+
pixels[i + 2] = gradient[j + 2];
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
},
|
|
273
|
+
|
|
274
|
+
_createCanvas: function () {
|
|
275
|
+
if (typeof document !== "undefined") {
|
|
276
|
+
return document.createElement("canvas");
|
|
277
|
+
} else {
|
|
278
|
+
// create a new canvas instance in node.js
|
|
279
|
+
// the canvas class needs to have a default constructor without any parameter
|
|
280
|
+
return new this._canvas.constructor();
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
};
|
package/src/HexBin.css
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
.chart_HexBin .hexagon {
|
|
2
|
-
fill: none;
|
|
3
|
-
stroke: #000;
|
|
4
|
-
stroke-width: .5px;
|
|
5
|
-
}
|
|
6
|
-
|
|
7
|
-
.chart_HexBin .hexagon.selected {
|
|
8
|
-
stroke: red;
|
|
1
|
+
.chart_HexBin .hexagon {
|
|
2
|
+
fill: none;
|
|
3
|
+
stroke: #000;
|
|
4
|
+
stroke-width: .5px;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
.chart_HexBin .hexagon.selected {
|
|
8
|
+
stroke: red;
|
|
9
9
|
}
|