gridviz 2.1.8 → 2.1.9

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 (57) hide show
  1. package/README.md +125 -125
  2. package/dist/gridviz.js +8021 -8013
  3. package/dist/gridviz.min.js +1 -1
  4. package/package.json +77 -77
  5. package/src/App.js +705 -705
  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 +101 -101
  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 +129 -129
  16. package/src/Tooltip.js +180 -172
  17. package/src/dataset/COGeo.js +24 -24
  18. package/src/dataset/CSVGrid.js +104 -104
  19. package/src/dataset/GeoTIFF.js +127 -127
  20. package/src/dataset/GridTile.js +41 -41
  21. package/src/dataset/LGrid.js +54 -54
  22. package/src/dataset/TiledGrid.js +264 -264
  23. package/src/index.js +71 -71
  24. package/src/legend/ColorCategoryLegend.js +124 -124
  25. package/src/legend/ColorDiscreteLegend.js +103 -103
  26. package/src/legend/ColorLegend.js +124 -124
  27. package/src/legend/SegmentOrientationLegend.js +87 -87
  28. package/src/legend/SegmentWidthLegend.js +97 -97
  29. package/src/legend/SizeLegend.js +141 -141
  30. package/src/style/CompositionStyle.js +300 -300
  31. package/src/style/ContourStyle.js +54 -54
  32. package/src/style/DotDensityStyle.js +156 -156
  33. package/src/style/IsoFenceStyle.js +262 -262
  34. package/src/style/JoyPlotStyle.js +135 -135
  35. package/src/style/LegoStyle.js +112 -112
  36. package/src/style/MosaicStyle.js +107 -107
  37. package/src/style/NinjaStarStyle.js +125 -125
  38. package/src/style/PillarStyle.js +230 -230
  39. package/src/style/SegmentStyle.js +142 -142
  40. package/src/style/ShapeColorSizeStyle.js +126 -126
  41. package/src/style/SideCatStyle.js +172 -172
  42. package/src/style/SideStyle.js +216 -216
  43. package/src/style/SquareColorCatWGLStyle.js +120 -120
  44. package/src/style/SquareColorWGLStyle.js +146 -146
  45. package/src/style/StrokeStyle.js +131 -131
  46. package/src/style/TanakaStyle.js +160 -160
  47. package/src/style/TextStyle.js +132 -132
  48. package/src/style/TimeSeriesStyle.js +250 -250
  49. package/src/utils/Utils.js +35 -35
  50. package/src/utils/WebGLRectangleColoring.js +106 -106
  51. package/src/utils/WebGLSquareColoring.js +118 -118
  52. package/src/utils/WebGLSquareColoringAdvanced.js +230 -230
  53. package/src/utils/WebGLSquareColoringCatAdvanced.js +133 -133
  54. package/src/utils/WebGLSquareColoringKS.js +153 -153
  55. package/src/utils/stretching.js +131 -131
  56. package/src/utils/webGLUtils.js +71 -71
  57. 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.
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
- //draw in geo coordinates
80
- cg.setCanvasTransform()
81
-
82
- //half resolution
83
- const r2 = r / 2
84
-
85
- //get offset
86
- // @ts-ignore
87
- const offset = this.offset(undefined, r, zf), dx = offset.dx, dy = offset.dy
88
-
89
- //height
90
- /** @type {function(number,number,import("../Style").Stat|undefined,number):number} */
91
- let h_ = this.height
92
-
93
- //make sides
94
- /** @type {Array.<Side>} */
95
- const sides = []
96
-
97
- //make horizontal sides - except when angle%180=0
98
- //sort cells by x and y
99
- if (this.angle % 180 != 90 && this.sVert) {
100
- cells.sort((c1, c2) => (c2.x == c1.x ? c1.y - c2.y : c1.x - c2.x))
101
- let c1 = cells[0]
102
- for (let i = 1; i < cells.length; i++) {
103
- let c2 = cells[i]
104
-
105
- if ((c1.y + r == c2.y) && (c1.x == c2.x))
106
- //cells in same column and touch along horizontal side
107
- //make shared side
108
- sides.push({ x: c1.x + r2, y: c2.y, or: 'h', c1: c1, c2: c2 })
109
- else {
110
- //cells do not touch along horizontal side
111
- //make two sides: top one for c1, bottom for c2
112
- sides.push({ x: c1.x + r2, y: c1.y + r, or: 'h', c1: c1, c2: undefined })
113
- sides.push({ x: c2.x + r2, y: c2.y, or: 'h', c1: undefined, c2: c2 })
114
- }
115
-
116
- c1 = c2
117
- }
118
- }
119
-
120
- //make vertical sides - except when angle%180=90
121
- //sort cells by y and x
122
- if (this.angle % 180 != 0 && this.sHor) {
123
- cells.sort((c1, c2) => (c2.y == c1.y ? c1.x - c2.x : c1.y - c2.y))
124
- let c1 = cells[0]
125
- for (let i = 1; i < cells.length; i++) {
126
- let c2 = cells[i]
127
-
128
- if ((c1.x + r == c2.x) && (c1.y == c2.y))
129
- //cells in same row and touch along vertical side
130
- //make shared side
131
- sides.push({ x: c2.x, y: c1.y + r2, or: 'v', c1: c1, c2: c2 })
132
- else {
133
- //cells do not touch along vertical side
134
- //make two sides: right one for c1, left for c2
135
- sides.push({ x: c1.x + r, y: c1.y + r2, or: 'v', c1: c1, c2: undefined })
136
- sides.push({ x: c2.x, y: c2.y + r2, or: 'v', c1: undefined, c2: c2 })
137
- }
138
-
139
- c1 = c2
140
- }
141
- }
142
-
143
- //
144
- if (sides.length == 0) return
145
-
146
- //angle in radians
147
- const aRad = this.angle * Math.PI / 180, cos = Math.cos(aRad), sin = Math.sin(aRad)
148
-
149
- //draw in geo coordinates
150
- cg.setCanvasTransform()
151
-
152
- //sort sides so that the back ones are drawn first. This depends on the angle
153
- if (Math.abs(this.angle) <= 90)
154
- //north east ones are drown first
155
- sides.sort((s1, s2) => (Math.hypot(s2.x, s2.y) - Math.hypot(s1.x, s1.y)))
156
- else if (this.angle > 90)
157
- //north west ones are drown first
158
- sides.sort((s1, s2) => (Math.hypot(s2.x - cg.extGeo.xMax, s2.y) - Math.hypot(s1.x - cg.extGeo.xMax, s1.y)))
159
- else if (this.angle < 90)
160
- //south west ones are drown first
161
- sides.sort((s1, s2) => (Math.hypot(s2.x - cg.extGeo.xMax, s2.y - cg.extGeo.yMax) - Math.hypot(s1.x - cg.extGeo.xMax, s1.y - cg.extGeo.yMax)))
162
-
163
- //draw sides
164
- for (let s of sides) {
165
-
166
- //heights - in geo
167
- /** @type {number} */
168
- const hG1 = s.c1 ? h_(s.c1[this.heightCol], r, stat, zf) : 0
169
- /** @type {number} */
170
- const hG2 = s.c2 ? h_(s.c2[this.heightCol], r, stat, zf) : 0
171
-
172
- //compute totals for both cells
173
- const total1 = computeTotal(s.c1, cats)
174
- const total2 = computeTotal(s.c2, cats)
175
- if (total1 == 0 && total2 == 0) continue
176
-
177
- let cumul1 = 0, cumul2 = 0
178
- for (let [column, color] of Object.entries(this.color)) {
179
- //draw stripe of side s and category column
180
-
181
- //get values for both cells
182
- let v1 = s.c1 ? +s.c1[column] : 0
183
- let v2 = s.c2 ? +s.c2[column] : 0
184
- if (v1 == 0 && v2 == 0) continue
185
-
186
- //compute heights
187
- const h1 = total1 > 0 ? hG1 * cumul1 / total1 : 0
188
- const h1n = total1 > 0 ? hG1 * (cumul1 + v1) / total1 : 0
189
- const h2 = total2 > 0 ? hG2 * cumul2 / total2 : 0
190
- const h2n = total2 > 0 ? hG2 * (cumul2 + v2) / total2 : 0
191
-
192
- //make path
193
- cg.ctx.beginPath()
194
- if (s.or == "h") {
195
- //horizontal side - vertical section
196
- //bottom left
197
- cg.ctx.moveTo(s.x + h1 * cos + dx, s.y - r2 + h1 * sin + dy)
198
- //top left
199
- cg.ctx.lineTo(s.x + h2 * cos + dx, s.y + r2 + h2 * sin + dy)
200
- //top right
201
- cg.ctx.lineTo(s.x + h2n * cos + dx, s.y + r2 + h2n * sin + dy)
202
- //bottom right
203
- cg.ctx.lineTo(s.x + h1n * cos + dx, s.y - r2 + h1n * sin + dy)
204
- } else {
205
- //vertical side - horizontal section
206
- //bottom left
207
- cg.ctx.moveTo(s.x - r2 + h1 * cos + dx, s.y + h1 * sin + dy)
208
- //bottom right
209
- cg.ctx.lineTo(s.x + r2 + h2 * cos + dx, s.y + h2 * sin + dy)
210
- //top right
211
- cg.ctx.lineTo(s.x + r2 + h2n * cos + dx, s.y + h2n * sin + dy)
212
- //top left
213
- cg.ctx.lineTo(s.x - r2 + h1n * cos + dx, s.y + h1n * sin + dy)
214
- }
215
- cg.ctx.closePath()
216
-
217
- //fill
218
- cg.ctx.fillStyle = color
219
- cg.ctx.fill()
220
-
221
- cumul1 += v1
222
- cumul2 += v2
223
- //break
224
- }
225
- }
226
-
227
- //draw corner lines
228
- for (let c of cells) {
229
-
230
- //line style
231
- const lw = this.cornerLineWidth ? this.cornerLineWidth(c, r, zf, this.angle) : 0.8 * zf
232
- if (lw == 0) continue
233
- cg.ctx.strokeStyle = this.cornerLineStrokeColor ? this.cornerLineStrokeColor(c, r, zf, this.angle) : "#333"
234
- cg.ctx.lineWidth = lw
235
-
236
- //height - in geo
237
- const hG = h_(c[this.heightCol], r, stat, zf)
238
-
239
- cg.ctx.beginPath()
240
- cg.ctx.moveTo(c.x + r2 + dx, c.y + r2 + dy)
241
- cg.ctx.lineTo(c.x + r2 + hG * cos + dx, c.y + r2 + hG * sin + dy)
242
- cg.ctx.closePath()
243
- cg.ctx.stroke()
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.
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
+ //draw in geo coordinates
80
+ cg.setCanvasTransform()
81
+
82
+ //half resolution
83
+ const r2 = r / 2
84
+
85
+ //get offset
86
+ // @ts-ignore
87
+ const offset = this.offset(undefined, r, zf), dx = offset.dx, dy = offset.dy
88
+
89
+ //height
90
+ /** @type {function(number,number,import("../Style").Stat|undefined,number):number} */
91
+ let h_ = this.height
92
+
93
+ //make sides
94
+ /** @type {Array.<Side>} */
95
+ const sides = []
96
+
97
+ //make horizontal sides - except when angle%180=0
98
+ //sort cells by x and y
99
+ if (this.angle % 180 != 90 && this.sVert) {
100
+ cells.sort((c1, c2) => (c2.x == c1.x ? c1.y - c2.y : c1.x - c2.x))
101
+ let c1 = cells[0]
102
+ for (let i = 1; i < cells.length; i++) {
103
+ let c2 = cells[i]
104
+
105
+ if ((c1.y + r == c2.y) && (c1.x == c2.x))
106
+ //cells in same column and touch along horizontal side
107
+ //make shared side
108
+ sides.push({ x: c1.x + r2, y: c2.y, or: 'h', c1: c1, c2: c2 })
109
+ else {
110
+ //cells do not touch along horizontal side
111
+ //make two sides: top one for c1, bottom for c2
112
+ sides.push({ x: c1.x + r2, y: c1.y + r, or: 'h', c1: c1, c2: undefined })
113
+ sides.push({ x: c2.x + r2, y: c2.y, or: 'h', c1: undefined, c2: c2 })
114
+ }
115
+
116
+ c1 = c2
117
+ }
118
+ }
119
+
120
+ //make vertical sides - except when angle%180=90
121
+ //sort cells by y and x
122
+ if (this.angle % 180 != 0 && this.sHor) {
123
+ cells.sort((c1, c2) => (c2.y == c1.y ? c1.x - c2.x : c1.y - c2.y))
124
+ let c1 = cells[0]
125
+ for (let i = 1; i < cells.length; i++) {
126
+ let c2 = cells[i]
127
+
128
+ if ((c1.x + r == c2.x) && (c1.y == c2.y))
129
+ //cells in same row and touch along vertical side
130
+ //make shared side
131
+ sides.push({ x: c2.x, y: c1.y + r2, or: 'v', c1: c1, c2: c2 })
132
+ else {
133
+ //cells do not touch along vertical side
134
+ //make two sides: right one for c1, left for c2
135
+ sides.push({ x: c1.x + r, y: c1.y + r2, or: 'v', c1: c1, c2: undefined })
136
+ sides.push({ x: c2.x, y: c2.y + r2, or: 'v', c1: undefined, c2: c2 })
137
+ }
138
+
139
+ c1 = c2
140
+ }
141
+ }
142
+
143
+ //
144
+ if (sides.length == 0) return
145
+
146
+ //angle in radians
147
+ const aRad = this.angle * Math.PI / 180, cos = Math.cos(aRad), sin = Math.sin(aRad)
148
+
149
+ //draw in geo coordinates
150
+ cg.setCanvasTransform()
151
+
152
+ //sort sides so that the back ones are drawn first. This depends on the angle
153
+ if (Math.abs(this.angle) <= 90)
154
+ //north east ones are drown first
155
+ sides.sort((s1, s2) => (Math.hypot(s2.x, s2.y) - Math.hypot(s1.x, s1.y)))
156
+ else if (this.angle > 90)
157
+ //north west ones are drown first
158
+ sides.sort((s1, s2) => (Math.hypot(s2.x - cg.extGeo.xMax, s2.y) - Math.hypot(s1.x - cg.extGeo.xMax, s1.y)))
159
+ else if (this.angle < 90)
160
+ //south west ones are drown first
161
+ sides.sort((s1, s2) => (Math.hypot(s2.x - cg.extGeo.xMax, s2.y - cg.extGeo.yMax) - Math.hypot(s1.x - cg.extGeo.xMax, s1.y - cg.extGeo.yMax)))
162
+
163
+ //draw sides
164
+ for (let s of sides) {
165
+
166
+ //heights - in geo
167
+ /** @type {number} */
168
+ const hG1 = s.c1 ? h_(s.c1[this.heightCol], r, stat, zf) : 0
169
+ /** @type {number} */
170
+ const hG2 = s.c2 ? h_(s.c2[this.heightCol], r, stat, zf) : 0
171
+
172
+ //compute totals for both cells
173
+ const total1 = computeTotal(s.c1, cats)
174
+ const total2 = computeTotal(s.c2, cats)
175
+ if (total1 == 0 && total2 == 0) continue
176
+
177
+ let cumul1 = 0, cumul2 = 0
178
+ for (let [column, color] of Object.entries(this.color)) {
179
+ //draw stripe of side s and category column
180
+
181
+ //get values for both cells
182
+ let v1 = s.c1 ? +s.c1[column] : 0
183
+ let v2 = s.c2 ? +s.c2[column] : 0
184
+ if (v1 == 0 && v2 == 0) continue
185
+
186
+ //compute heights
187
+ const h1 = total1 > 0 ? hG1 * cumul1 / total1 : 0
188
+ const h1n = total1 > 0 ? hG1 * (cumul1 + v1) / total1 : 0
189
+ const h2 = total2 > 0 ? hG2 * cumul2 / total2 : 0
190
+ const h2n = total2 > 0 ? hG2 * (cumul2 + v2) / total2 : 0
191
+
192
+ //make path
193
+ cg.ctx.beginPath()
194
+ if (s.or == "h") {
195
+ //horizontal side - vertical section
196
+ //bottom left
197
+ cg.ctx.moveTo(s.x + h1 * cos + dx, s.y - r2 + h1 * sin + dy)
198
+ //top left
199
+ cg.ctx.lineTo(s.x + h2 * cos + dx, s.y + r2 + h2 * sin + dy)
200
+ //top right
201
+ cg.ctx.lineTo(s.x + h2n * cos + dx, s.y + r2 + h2n * sin + dy)
202
+ //bottom right
203
+ cg.ctx.lineTo(s.x + h1n * cos + dx, s.y - r2 + h1n * sin + dy)
204
+ } else {
205
+ //vertical side - horizontal section
206
+ //bottom left
207
+ cg.ctx.moveTo(s.x - r2 + h1 * cos + dx, s.y + h1 * sin + dy)
208
+ //bottom right
209
+ cg.ctx.lineTo(s.x + r2 + h2 * cos + dx, s.y + h2 * sin + dy)
210
+ //top right
211
+ cg.ctx.lineTo(s.x + r2 + h2n * cos + dx, s.y + h2n * sin + dy)
212
+ //top left
213
+ cg.ctx.lineTo(s.x - r2 + h1n * cos + dx, s.y + h1n * sin + dy)
214
+ }
215
+ cg.ctx.closePath()
216
+
217
+ //fill
218
+ cg.ctx.fillStyle = color
219
+ cg.ctx.fill()
220
+
221
+ cumul1 += v1
222
+ cumul2 += v2
223
+ //break
224
+ }
225
+ }
226
+
227
+ //draw corner lines
228
+ for (let c of cells) {
229
+
230
+ //line style
231
+ const lw = this.cornerLineWidth ? this.cornerLineWidth(c, r, zf, this.angle) : 0.8 * zf
232
+ if (lw == 0) continue
233
+ cg.ctx.strokeStyle = this.cornerLineStrokeColor ? this.cornerLineStrokeColor(c, r, zf, this.angle) : "#333"
234
+ cg.ctx.lineWidth = lw
235
+
236
+ //height - in geo
237
+ const hG = h_(c[this.heightCol], r, stat, zf)
238
+
239
+ cg.ctx.beginPath()
240
+ cg.ctx.moveTo(c.x + r2 + dx, c.y + r2 + dy)
241
+ cg.ctx.lineTo(c.x + r2 + hG * cos + dx, c.y + r2 + hG * sin + dy)
242
+ cg.ctx.closePath()
243
+ cg.ctx.stroke()
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
+ }