gridviz 2.1.13 → 2.1.14

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.
Files changed (60) hide show
  1. package/README.md +124 -125
  2. package/dist/gridviz.js +8341 -8339
  3. package/dist/gridviz.min.js +1 -1
  4. package/package.json +77 -77
  5. package/src/App.js +758 -758
  6. package/src/BackgroundLayer.js +182 -182
  7. package/src/BackgroundLayerWMS.js +131 -131
  8. package/src/Dataset.js +76 -76
  9. package/src/DatasetComponent.js +113 -113
  10. package/src/GeoCanvas.js +420 -420
  11. package/src/LabelLayer.js +190 -190
  12. package/src/Layer.js +103 -103
  13. package/src/Legend.js +86 -86
  14. package/src/LineLayer.js +150 -150
  15. package/src/Style.js +131 -129
  16. package/src/Tooltip.js +180 -180
  17. package/src/button/Button.js +54 -54
  18. package/src/button/FullscreenButton.js +98 -98
  19. package/src/button/ZoomButtons.js +72 -72
  20. package/src/dataset/COGeo.js +24 -24
  21. package/src/dataset/CSVGrid.js +104 -104
  22. package/src/dataset/GeoTIFF.js +127 -127
  23. package/src/dataset/GridTile.js +41 -41
  24. package/src/dataset/LGrid.js +54 -54
  25. package/src/dataset/TiledGrid.js +264 -264
  26. package/src/index.js +71 -71
  27. package/src/legend/ColorCategoryLegend.js +124 -124
  28. package/src/legend/ColorDiscreteLegend.js +103 -103
  29. package/src/legend/ColorLegend.js +128 -128
  30. package/src/legend/SegmentOrientationLegend.js +87 -87
  31. package/src/legend/SegmentWidthLegend.js +101 -101
  32. package/src/legend/SizeLegend.js +141 -141
  33. package/src/style/CompositionStyle.js +300 -300
  34. package/src/style/ContourStyle.js +54 -54
  35. package/src/style/DotDensityStyle.js +156 -156
  36. package/src/style/IsoFenceStyle.js +262 -262
  37. package/src/style/JoyPlotStyle.js +135 -135
  38. package/src/style/LegoStyle.js +112 -112
  39. package/src/style/MosaicStyle.js +107 -107
  40. package/src/style/NinjaStarStyle.js +125 -125
  41. package/src/style/PillarStyle.js +230 -230
  42. package/src/style/SegmentStyle.js +142 -142
  43. package/src/style/ShapeColorSizeStyle.js +152 -152
  44. package/src/style/SideCatStyle.js +172 -172
  45. package/src/style/SideStyle.js +216 -216
  46. package/src/style/SquareColorCatWGLStyle.js +120 -120
  47. package/src/style/SquareColorWGLStyle.js +146 -146
  48. package/src/style/StrokeStyle.js +131 -131
  49. package/src/style/TanakaStyle.js +160 -160
  50. package/src/style/TextStyle.js +132 -132
  51. package/src/style/TimeSeriesStyle.js +259 -259
  52. package/src/utils/Utils.js +35 -35
  53. package/src/utils/WebGLRectangleColoring.js +106 -106
  54. package/src/utils/WebGLSquareColoring.js +118 -118
  55. package/src/utils/WebGLSquareColoringAdvanced.js +230 -230
  56. package/src/utils/WebGLSquareColoringCatAdvanced.js +133 -133
  57. package/src/utils/WebGLSquareColoringKS.js +153 -153
  58. package/src/utils/stretching.js +131 -131
  59. package/src/utils/webGLUtils.js +71 -71
  60. package/src/utils/zoomUtils.js +142 -142
@@ -1,262 +1,262 @@
1
- //@ts-check
2
- 'use strict'
3
-
4
- import { Style } from '../Style.js'
5
-
6
- /** @typedef {{x:number,y:number,or:"v"|"h",c1:import('../Dataset.js').Cell|undefined,c2:import('../Dataset.js').Cell|undefined}} Side */
7
-
8
- /**
9
- * @author Julien Gaffuri
10
- */
11
- export class IsoFenceStyle extends Style {
12
-
13
- /** @param {object} opts */
14
- constructor(opts) {
15
- super(opts)
16
- opts = opts || {}
17
-
18
- /**
19
- * The dictionary (string -> color) which give the color of each category.
20
- * @type {object} */
21
- this.color = opts.color
22
-
23
- /** The column where to get the height values.
24
- * @type {string} */
25
- this.heightCol = opts.heightCol
26
-
27
- /** A function returning the height of a cell.
28
- * @type {function(number,number,import("../Style").Stat|undefined,number):number} */
29
- this.height = opts.height || ((v, r, s, zf) => r * 0.4)
30
-
31
- /** The perspective angle, in degree, within [-180,180], from [O,x] axis.
32
- * @type {number} */
33
- this.angle = opts.angle != undefined ? opts.angle : 50
34
-
35
- /** A function returning the corner line stroke style.
36
- * @type {function(import('../Dataset.js').Cell,number,number,number):string} */
37
- this.cornerLineStrokeColor = opts.cornerLineStrokeColor || ((c, r, zf, angle) => "#999")
38
-
39
- /** A function returning the corner line width.
40
- * @type {function(import('../Dataset.js').Cell,number,number,number):number} */
41
- this.cornerLineWidth = opts.cornerLineWidth || ((c, r, zf, angle) => (angle % 90 == 0 ? 0 : 0.8 * zf))
42
-
43
- /**
44
- * Show vertical cross-sections.
45
- * @type {boolean} */
46
- this.sVert = opts.sVert != undefined ? opts.sVert : true
47
-
48
- /**
49
- * Show horizontal cross-sections.
50
- * @type {boolean} */
51
- this.sHor = opts.sHor != undefined ? opts.sHor : true
52
- }
53
-
54
- /**
55
- * Draw cells as segments.
56
- *
57
- * @param {Array.<import("../Dataset.js").Cell>} cells
58
- * @param {number} r
59
- * @param {import("../GeoCanvas.js").GeoCanvas} cg
60
- */
61
- draw(cells, r, cg) {
62
- //filter
63
- if (this.filter) cells = cells.filter(this.filter)
64
-
65
- //zoom factor
66
- const zf = cg.getZf()
67
-
68
- let stat
69
- if (this.heightCol) {
70
- //if size is used, sort cells by size so that the biggest are drawn first
71
- cells.sort((c1, c2) => c2[this.heightCol] - c1[this.heightCol])
72
- //and compute statistics
73
- stat = Style.getStatistics(cells, (c) => c[this.heightCol], true)
74
- }
75
-
76
- //nb categories - used for radar and agepyramid
77
- const cats = Object.keys(this.color)
78
-
79
- //half resolution
80
- const r2 = r / 2
81
-
82
- //get offset
83
- // @ts-ignore
84
- const offset = this.offset(undefined, r, zf), dx = offset.dx, dy = offset.dy
85
-
86
- //height
87
- /** @type {function(number,number,import("../Style").Stat|undefined,number):number} */
88
- let h_ = this.height
89
-
90
- //make sides
91
- /** @type {Array.<Side>} */
92
- const sides = []
93
-
94
- //make horizontal sides - except when angle%180=0
95
- //sort cells by x and y
96
- if (this.angle % 180 != 90 && this.sVert) {
97
- cells.sort((c1, c2) => (c2.x == c1.x ? c1.y - c2.y : c1.x - c2.x))
98
- let c1 = cells[0]
99
- for (let i = 1; i < cells.length; i++) {
100
- let c2 = cells[i]
101
-
102
- if ((c1.y + r == c2.y) && (c1.x == c2.x))
103
- //cells in same column and touch along horizontal side
104
- //make shared side
105
- sides.push({ x: c1.x + r2, y: c2.y, or: 'h', c1: c1, c2: c2 })
106
- else {
107
- //cells do not touch along horizontal side
108
- //make two sides: top one for c1, bottom for c2
109
- sides.push({ x: c1.x + r2, y: c1.y + r, or: 'h', c1: c1, c2: undefined })
110
- sides.push({ x: c2.x + r2, y: c2.y, or: 'h', c1: undefined, c2: c2 })
111
- }
112
-
113
- c1 = c2
114
- }
115
- }
116
-
117
- //make vertical sides - except when angle%180=90
118
- //sort cells by y and x
119
- if (this.angle % 180 != 0 && this.sHor) {
120
- cells.sort((c1, c2) => (c2.y == c1.y ? c1.x - c2.x : c1.y - c2.y))
121
- let c1 = cells[0]
122
- for (let i = 1; i < cells.length; i++) {
123
- let c2 = cells[i]
124
-
125
- if ((c1.x + r == c2.x) && (c1.y == c2.y))
126
- //cells in same row and touch along vertical side
127
- //make shared side
128
- sides.push({ x: c2.x, y: c1.y + r2, or: 'v', c1: c1, c2: c2 })
129
- else {
130
- //cells do not touch along vertical side
131
- //make two sides: right one for c1, left for c2
132
- sides.push({ x: c1.x + r, y: c1.y + r2, or: 'v', c1: c1, c2: undefined })
133
- sides.push({ x: c2.x, y: c2.y + r2, or: 'v', c1: undefined, c2: c2 })
134
- }
135
-
136
- c1 = c2
137
- }
138
- }
139
-
140
- //
141
- if (sides.length == 0) return
142
-
143
- //angle in radians
144
- const aRad = this.angle * Math.PI / 180, cos = Math.cos(aRad), sin = Math.sin(aRad)
145
-
146
- //sort sides so that the back ones are drawn first. This depends on the angle.
147
- //depending on distance to the reference corner point
148
- const xCorner = Math.abs(this.angle) < 90 ? cg.extGeo.xMin : cg.extGeo.xMax
149
- const yCorner = this.angle < 0 ? cg.extGeo.yMax : cg.extGeo.yMin
150
- sides.sort((s1, s2) => (Math.hypot(s2.x - xCorner, s2.y - yCorner) - Math.hypot(s1.x - xCorner, s1.y - yCorner)))
151
-
152
- //prepare function to draw corner line for a cell *c*
153
- const drawCornerLine = (c) => {
154
-
155
- if (!c) return
156
- //line style
157
- const lw = this.cornerLineWidth ? this.cornerLineWidth(c, r, zf, this.angle) : 0.8 * zf
158
- if (lw == 0) return
159
- cg.ctx.strokeStyle = this.cornerLineStrokeColor ? this.cornerLineStrokeColor(c, r, zf, this.angle) : "#333"
160
- cg.ctx.lineWidth = lw
161
-
162
- //height - in geo
163
- const hG = h_(c[this.heightCol], r, stat, zf)
164
-
165
- //draw line
166
- cg.ctx.beginPath()
167
- cg.ctx.moveTo(c.x + r2 + dx, c.y + r2 + dy)
168
- cg.ctx.lineTo(c.x + r2 + hG * cos + dx, c.y + r2 + hG * sin + dy)
169
- cg.ctx.stroke()
170
- }
171
-
172
- //draw in geo coordinates
173
- cg.setCanvasTransform()
174
-
175
- //draw sides
176
- cg.ctx.lineCap = "round";
177
- for (let s of sides) {
178
-
179
- //heights - in geo
180
- const hG1 = s.c1 ? h_(s.c1[this.heightCol], r, stat, zf) : 0,
181
- hG2 = s.c2 ? h_(s.c2[this.heightCol], r, stat, zf) : 0
182
-
183
- //compute totals for both cells
184
- const total1 = computeTotal(s.c1, cats),
185
- total2 = computeTotal(s.c2, cats)
186
- if (total1 == 0 && total2 == 0) continue
187
-
188
- let cumul1 = 0, cumul2 = 0
189
- for (let [column, color] of Object.entries(this.color)) {
190
- //draw stripe of side s and category column
191
-
192
- //get values for both cells
193
- let v1 = s.c1 ? +s.c1[column] : 0
194
- let v2 = s.c2 ? +s.c2[column] : 0
195
- if (v1 == 0 && v2 == 0) continue
196
-
197
- //compute heights
198
- const h1 = hG1 * cumul1 / total1 || 0
199
- const h1n = hG1 * (cumul1 + v1) / total1 || 0
200
- const h2 = hG2 * cumul2 / total2 || 0
201
- const h2n = hG2 * (cumul2 + v2) / total2 || 0
202
-
203
- //make path
204
- cg.ctx.beginPath()
205
- if (s.or == "h") {
206
- //horizontal side - vertical section
207
- //bottom left
208
- cg.ctx.moveTo(s.x + h1 * cos + dx, s.y - r2 + h1 * sin + dy)
209
- //top left
210
- cg.ctx.lineTo(s.x + h2 * cos + dx, s.y + r2 + h2 * sin + dy)
211
- //top right
212
- cg.ctx.lineTo(s.x + h2n * cos + dx, s.y + r2 + h2n * sin + dy)
213
- //bottom right
214
- cg.ctx.lineTo(s.x + h1n * cos + dx, s.y - r2 + h1n * sin + dy)
215
- } else {
216
- //vertical side - horizontal section
217
- //bottom left
218
- cg.ctx.moveTo(s.x - r2 + h1 * cos + dx, s.y + h1 * sin + dy)
219
- //bottom right
220
- cg.ctx.lineTo(s.x + r2 + h2 * cos + dx, s.y + h2 * sin + dy)
221
- //top right
222
- cg.ctx.lineTo(s.x + r2 + h2n * cos + dx, s.y + h2n * sin + dy)
223
- //top left
224
- cg.ctx.lineTo(s.x - r2 + h1n * cos + dx, s.y + h1n * sin + dy)
225
- }
226
- //cg.ctx.closePath()
227
-
228
- //fill
229
- cg.ctx.fillStyle = color
230
- cg.ctx.fill()
231
-
232
- cumul1 += v1
233
- cumul2 += v2
234
-
235
- //TODO draw only one line
236
- //draw corner line
237
- //if (s.or == "h") {
238
- drawCornerLine(s.c1)
239
- drawCornerLine(s.c2)
240
- //if (this.angle > 0 && s.or == "h") drawCornerLine(s.c2)
241
- //else drawCornerLine(s.c2)
242
- //}
243
- }
244
- }
245
-
246
- //update legends
247
- this.updateLegends({ style: this, r: r, zf: zf, sSize: stat })
248
- }
249
- }
250
-
251
-
252
-
253
- const computeTotal = (cell, categories) => {
254
- if (!cell) return 0
255
- let total = 0
256
- for (let column of categories) {
257
- const v = cell[column]
258
- if (!v) continue
259
- total += +v
260
- }
261
- return total || 0
262
- }
1
+ //@ts-check
2
+ 'use strict'
3
+
4
+ import { Style } from '../Style.js'
5
+
6
+ /** @typedef {{x:number,y:number,or:"v"|"h",c1:import('../Dataset.js').Cell|undefined,c2:import('../Dataset.js').Cell|undefined}} Side */
7
+
8
+ /**
9
+ * @author Julien Gaffuri
10
+ */
11
+ export class IsoFenceStyle extends Style {
12
+
13
+ /** @param {object} opts */
14
+ constructor(opts) {
15
+ super(opts)
16
+ opts = opts || {}
17
+
18
+ /**
19
+ * The dictionary (string -> color) which give the color of each category.
20
+ * @type {object} */
21
+ this.color = opts.color
22
+
23
+ /** The column where to get the height values.
24
+ * @type {string} */
25
+ this.heightCol = opts.heightCol
26
+
27
+ /** A function returning the height of a cell.
28
+ * @type {function(number,number,import("../Style").Stat|undefined,number):number} */
29
+ this.height = opts.height || ((v, r, s, zf) => r * 0.4)
30
+
31
+ /** The perspective angle, in degree, within [-180,180], from [O,x] axis.
32
+ * @type {number} */
33
+ this.angle = opts.angle != undefined ? opts.angle : 50
34
+
35
+ /** A function returning the corner line stroke style.
36
+ * @type {function(import('../Dataset.js').Cell,number,number,number):string} */
37
+ this.cornerLineStrokeColor = opts.cornerLineStrokeColor || ((c, r, zf, angle) => "#999")
38
+
39
+ /** A function returning the corner line width.
40
+ * @type {function(import('../Dataset.js').Cell,number,number,number):number} */
41
+ this.cornerLineWidth = opts.cornerLineWidth || ((c, r, zf, angle) => (angle % 90 == 0 ? 0 : 0.8 * zf))
42
+
43
+ /**
44
+ * Show vertical cross-sections.
45
+ * @type {boolean} */
46
+ this.sVert = opts.sVert != undefined ? opts.sVert : true
47
+
48
+ /**
49
+ * Show horizontal cross-sections.
50
+ * @type {boolean} */
51
+ this.sHor = opts.sHor != undefined ? opts.sHor : true
52
+ }
53
+
54
+ /**
55
+ * Draw cells as segments.
56
+ *
57
+ * @param {Array.<import("../Dataset.js").Cell>} cells
58
+ * @param {number} r
59
+ * @param {import("../GeoCanvas.js").GeoCanvas} cg
60
+ */
61
+ draw(cells, r, cg) {
62
+ //filter
63
+ if (this.filter) cells = cells.filter(this.filter)
64
+
65
+ //zoom factor
66
+ const zf = cg.getZf()
67
+
68
+ let stat
69
+ if (this.heightCol) {
70
+ //if size is used, sort cells by size so that the biggest are drawn first
71
+ cells.sort((c1, c2) => c2[this.heightCol] - c1[this.heightCol])
72
+ //and compute statistics
73
+ stat = Style.getStatistics(cells, (c) => c[this.heightCol], true)
74
+ }
75
+
76
+ //nb categories - used for radar and agepyramid
77
+ const cats = Object.keys(this.color)
78
+
79
+ //half resolution
80
+ const r2 = r / 2
81
+
82
+ //get offset
83
+ // @ts-ignore
84
+ const offset = this.offset(undefined, r, zf), dx = offset.dx, dy = offset.dy
85
+
86
+ //height
87
+ /** @type {function(number,number,import("../Style").Stat|undefined,number):number} */
88
+ let h_ = this.height
89
+
90
+ //make sides
91
+ /** @type {Array.<Side>} */
92
+ const sides = []
93
+
94
+ //make horizontal sides - except when angle%180=0
95
+ //sort cells by x and y
96
+ if (this.angle % 180 != 90 && this.sVert) {
97
+ cells.sort((c1, c2) => (c2.x == c1.x ? c1.y - c2.y : c1.x - c2.x))
98
+ let c1 = cells[0]
99
+ for (let i = 1; i < cells.length; i++) {
100
+ let c2 = cells[i]
101
+
102
+ if ((c1.y + r == c2.y) && (c1.x == c2.x))
103
+ //cells in same column and touch along horizontal side
104
+ //make shared side
105
+ sides.push({ x: c1.x + r2, y: c2.y, or: 'h', c1: c1, c2: c2 })
106
+ else {
107
+ //cells do not touch along horizontal side
108
+ //make two sides: top one for c1, bottom for c2
109
+ sides.push({ x: c1.x + r2, y: c1.y + r, or: 'h', c1: c1, c2: undefined })
110
+ sides.push({ x: c2.x + r2, y: c2.y, or: 'h', c1: undefined, c2: c2 })
111
+ }
112
+
113
+ c1 = c2
114
+ }
115
+ }
116
+
117
+ //make vertical sides - except when angle%180=90
118
+ //sort cells by y and x
119
+ if (this.angle % 180 != 0 && this.sHor) {
120
+ cells.sort((c1, c2) => (c2.y == c1.y ? c1.x - c2.x : c1.y - c2.y))
121
+ let c1 = cells[0]
122
+ for (let i = 1; i < cells.length; i++) {
123
+ let c2 = cells[i]
124
+
125
+ if ((c1.x + r == c2.x) && (c1.y == c2.y))
126
+ //cells in same row and touch along vertical side
127
+ //make shared side
128
+ sides.push({ x: c2.x, y: c1.y + r2, or: 'v', c1: c1, c2: c2 })
129
+ else {
130
+ //cells do not touch along vertical side
131
+ //make two sides: right one for c1, left for c2
132
+ sides.push({ x: c1.x + r, y: c1.y + r2, or: 'v', c1: c1, c2: undefined })
133
+ sides.push({ x: c2.x, y: c2.y + r2, or: 'v', c1: undefined, c2: c2 })
134
+ }
135
+
136
+ c1 = c2
137
+ }
138
+ }
139
+
140
+ //
141
+ if (sides.length == 0) return
142
+
143
+ //angle in radians
144
+ const aRad = this.angle * Math.PI / 180, cos = Math.cos(aRad), sin = Math.sin(aRad)
145
+
146
+ //sort sides so that the back ones are drawn first. This depends on the angle.
147
+ //depending on distance to the reference corner point
148
+ const xCorner = Math.abs(this.angle) < 90 ? cg.extGeo.xMin : cg.extGeo.xMax
149
+ const yCorner = this.angle < 0 ? cg.extGeo.yMax : cg.extGeo.yMin
150
+ sides.sort((s1, s2) => (Math.hypot(s2.x - xCorner, s2.y - yCorner) - Math.hypot(s1.x - xCorner, s1.y - yCorner)))
151
+
152
+ //prepare function to draw corner line for a cell *c*
153
+ const drawCornerLine = (c) => {
154
+
155
+ if (!c) return
156
+ //line style
157
+ const lw = this.cornerLineWidth ? this.cornerLineWidth(c, r, zf, this.angle) : 0.8 * zf
158
+ if (lw == 0) return
159
+ cg.ctx.strokeStyle = this.cornerLineStrokeColor ? this.cornerLineStrokeColor(c, r, zf, this.angle) : "#333"
160
+ cg.ctx.lineWidth = lw
161
+
162
+ //height - in geo
163
+ const hG = h_(c[this.heightCol], r, stat, zf)
164
+
165
+ //draw line
166
+ cg.ctx.beginPath()
167
+ cg.ctx.moveTo(c.x + r2 + dx, c.y + r2 + dy)
168
+ cg.ctx.lineTo(c.x + r2 + hG * cos + dx, c.y + r2 + hG * sin + dy)
169
+ cg.ctx.stroke()
170
+ }
171
+
172
+ //draw in geo coordinates
173
+ cg.setCanvasTransform()
174
+
175
+ //draw sides
176
+ cg.ctx.lineCap = "round";
177
+ for (let s of sides) {
178
+
179
+ //heights - in geo
180
+ const hG1 = s.c1 ? h_(s.c1[this.heightCol], r, stat, zf) : 0,
181
+ hG2 = s.c2 ? h_(s.c2[this.heightCol], r, stat, zf) : 0
182
+
183
+ //compute totals for both cells
184
+ const total1 = computeTotal(s.c1, cats),
185
+ total2 = computeTotal(s.c2, cats)
186
+ if (total1 == 0 && total2 == 0) continue
187
+
188
+ let cumul1 = 0, cumul2 = 0
189
+ for (let [column, color] of Object.entries(this.color)) {
190
+ //draw stripe of side s and category column
191
+
192
+ //get values for both cells
193
+ let v1 = s.c1 ? +s.c1[column] : 0
194
+ let v2 = s.c2 ? +s.c2[column] : 0
195
+ if (v1 == 0 && v2 == 0) continue
196
+
197
+ //compute heights
198
+ const h1 = hG1 * cumul1 / total1 || 0
199
+ const h1n = hG1 * (cumul1 + v1) / total1 || 0
200
+ const h2 = hG2 * cumul2 / total2 || 0
201
+ const h2n = hG2 * (cumul2 + v2) / total2 || 0
202
+
203
+ //make path
204
+ cg.ctx.beginPath()
205
+ if (s.or == "h") {
206
+ //horizontal side - vertical section
207
+ //bottom left
208
+ cg.ctx.moveTo(s.x + h1 * cos + dx, s.y - r2 + h1 * sin + dy)
209
+ //top left
210
+ cg.ctx.lineTo(s.x + h2 * cos + dx, s.y + r2 + h2 * sin + dy)
211
+ //top right
212
+ cg.ctx.lineTo(s.x + h2n * cos + dx, s.y + r2 + h2n * sin + dy)
213
+ //bottom right
214
+ cg.ctx.lineTo(s.x + h1n * cos + dx, s.y - r2 + h1n * sin + dy)
215
+ } else {
216
+ //vertical side - horizontal section
217
+ //bottom left
218
+ cg.ctx.moveTo(s.x - r2 + h1 * cos + dx, s.y + h1 * sin + dy)
219
+ //bottom right
220
+ cg.ctx.lineTo(s.x + r2 + h2 * cos + dx, s.y + h2 * sin + dy)
221
+ //top right
222
+ cg.ctx.lineTo(s.x + r2 + h2n * cos + dx, s.y + h2n * sin + dy)
223
+ //top left
224
+ cg.ctx.lineTo(s.x - r2 + h1n * cos + dx, s.y + h1n * sin + dy)
225
+ }
226
+ //cg.ctx.closePath()
227
+
228
+ //fill
229
+ cg.ctx.fillStyle = color
230
+ cg.ctx.fill()
231
+
232
+ cumul1 += v1
233
+ cumul2 += v2
234
+
235
+ //TODO draw only one line
236
+ //draw corner line
237
+ //if (s.or == "h") {
238
+ drawCornerLine(s.c1)
239
+ drawCornerLine(s.c2)
240
+ //if (this.angle > 0 && s.or == "h") drawCornerLine(s.c2)
241
+ //else drawCornerLine(s.c2)
242
+ //}
243
+ }
244
+ }
245
+
246
+ //update legends
247
+ this.updateLegends({ style: this, r: r, zf: zf, sSize: stat })
248
+ }
249
+ }
250
+
251
+
252
+
253
+ const computeTotal = (cell, categories) => {
254
+ if (!cell) return 0
255
+ let total = 0
256
+ for (let column of categories) {
257
+ const v = cell[column]
258
+ if (!v) continue
259
+ total += +v
260
+ }
261
+ return total || 0
262
+ }