gridviz 3.0.34 → 3.0.36

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 (67) hide show
  1. package/README.md +129 -129
  2. package/dist/gridviz.js +17487 -17471
  3. package/dist/gridviz.min.js +2 -2
  4. package/package.json +73 -73
  5. package/src/button/Button.js +67 -67
  6. package/src/button/FullscreenButton.js +121 -121
  7. package/src/button/ZoomButtons.js +103 -103
  8. package/src/core/Dataset.js +154 -154
  9. package/src/core/Drawable.js +78 -78
  10. package/src/core/GeoCanvas.js +443 -443
  11. package/src/core/Layer.js +23 -23
  12. package/src/core/Legend.js +108 -108
  13. package/src/core/Map.js +568 -552
  14. package/src/core/MultiResolutionDataset.js +93 -93
  15. package/src/core/Style.js +98 -98
  16. package/src/core/Tooltip.js +182 -182
  17. package/src/dataset/COGeo.js +25 -25
  18. package/src/dataset/CSVGrid.js +112 -112
  19. package/src/dataset/GeoTIFF.js +122 -122
  20. package/src/dataset/JSGrid.js +55 -55
  21. package/src/dataset/TiledGrid.js +302 -302
  22. package/src/dataset/worker.js +18 -18
  23. package/src/index.js +79 -79
  24. package/src/layer/BackgroundLayer.js +173 -173
  25. package/src/layer/BackgroundLayerImage.js +81 -81
  26. package/src/layer/BackgroundLayerWMS.js +104 -104
  27. package/src/layer/GeoJSONLayer.js +276 -276
  28. package/src/layer/GridLayer.js +166 -166
  29. package/src/layer/LabelLayer.js +194 -194
  30. package/src/legend/ColorCategoryLegend.js +109 -109
  31. package/src/legend/ColorDiscreteLegend.js +100 -100
  32. package/src/legend/ColorLegend.js +115 -115
  33. package/src/legend/OrientationLegend.js +89 -89
  34. package/src/legend/SizeLegend.js +224 -224
  35. package/src/legend/TernaryLegend.js +302 -302
  36. package/src/style/CompositionStyle.js +291 -291
  37. package/src/style/DotDensityStyle.js +133 -133
  38. package/src/style/ImageStyle.js +100 -100
  39. package/src/style/Interpolator.js +258 -258
  40. package/src/style/IsoFenceStyle.js +217 -217
  41. package/src/style/JoyPlotStyle.js +180 -180
  42. package/src/style/LegoStyle.js +132 -132
  43. package/src/style/MosaicStyle.js +99 -99
  44. package/src/style/NinjaStarStyle.js +102 -102
  45. package/src/style/PillarStyle.js +207 -207
  46. package/src/style/SegmentStyle.js +105 -105
  47. package/src/style/ShadingRayStyle.js +280 -280
  48. package/src/style/ShadingStyle.js +72 -72
  49. package/src/style/ShapeColorSizeStyle.js +147 -147
  50. package/src/style/SideCategoryStyle.js +112 -112
  51. package/src/style/SideStyle.js +217 -217
  52. package/src/style/SideTanakaStyle.js +154 -154
  53. package/src/style/SquareColorCategoryWebGLStyle.js +307 -294
  54. package/src/style/SquareColorWebGLStyle.js +126 -126
  55. package/src/style/StrokeStyle.js +135 -135
  56. package/src/style/TextStyle.js +110 -110
  57. package/src/style/TimeSeriesStyle.js +222 -222
  58. package/src/utils/WebGLRectangleColoring.js +106 -106
  59. package/src/utils/WebGLSquareColoring.js +118 -118
  60. package/src/utils/WebGLSquareColoringAdvanced.js +227 -227
  61. package/src/utils/WebGLSquareColoringKS.js +153 -153
  62. package/src/utils/scale.js +255 -255
  63. package/src/utils/stretching.js +120 -120
  64. package/src/utils/ternary.js +208 -208
  65. package/src/utils/utils.js +125 -125
  66. package/src/utils/webGLUtils.js +73 -73
  67. package/src/utils/zoomUtils.js +142 -142
@@ -1,110 +1,110 @@
1
- //@ts-check
2
- 'use strict'
3
-
4
- import { Style } from '../core/Style.js'
5
-
6
- /**
7
- * @module style
8
- * @author Julien Gaffuri
9
- */
10
- export class TextStyle extends Style {
11
- /** @param {object} opts */
12
- constructor(opts) {
13
- super(opts)
14
- opts = opts || {}
15
-
16
- /** A function returning the text of a cell.
17
- * @type {function(import('../core/Dataset.js').Cell, number, number, object):string} */
18
- this.text = opts.text || (() => 'X') //(c,r,z,vs) => {}
19
-
20
- /** A function returning the color of the cell.
21
- * @type {function(import('../core/Dataset.js').Cell, number, number, object):string} */
22
- this.color = opts.color || (() => 'black') //(c,r,z,vs) => {}
23
-
24
- /** A function returning the font size of a cell in geo unit.
25
- * @type {function(import('../core/Dataset.js').Cell, number, number,object):number} */
26
- this.fontSize = opts.fontSize || ((cell, resolution) => resolution) //(c,r,z,vs) => {}
27
-
28
- /** The text font family.
29
- * @type {function(import('../core/Dataset.js').Cell, number, number, object):string} */
30
- this.fontFamily = opts.fontFamily || (() => 'Arial')
31
-
32
- /** The text font weight.
33
- * @type {function(import('../core/Dataset.js').Cell, number, number, object):string} */
34
- this.fontWeight = opts.fontWeight || (() => 'bold')
35
- }
36
-
37
- /**
38
- * Draw cells as text.
39
- *
40
- * @param {Array.<import("../core/Dataset").Cell>} cells
41
- * @param {import("../core/GeoCanvas").GeoCanvas} geoCanvas
42
- * @param {number} resolution
43
- */
44
- draw(cells, geoCanvas, resolution) {
45
- //filter
46
- if (this.filter) cells = cells.filter(this.filter)
47
-
48
- //
49
- const z = geoCanvas.view.z
50
- const ctx = geoCanvas.offscreenCtx
51
- //get view scale
52
- const viewScale = this.viewScale ? this.viewScale(cells, resolution, z) : undefined
53
-
54
- //draw with HTML canvas
55
- //in screen coordinates
56
- geoCanvas.initCanvasTransform()
57
-
58
- for (let cell of cells) {
59
- //get cell text
60
- const text = this.text ? this.text(cell, resolution, z, viewScale) : undefined
61
- if (text == undefined || text == null || text + '' === '') continue
62
-
63
- //color
64
- const col = this.color ? this.color(cell, resolution, z, viewScale) : undefined
65
- if (!col) continue
66
- ctx.fillStyle = col
67
-
68
- //font size
69
- //size - in pixel unit
70
- const fontSizePix = this.fontSize(cell, resolution, z, viewScale) / z
71
- if (!fontSizePix) continue
72
-
73
- //set font
74
- const fontFamily = this.fontFamily ? this.fontFamily(cell, resolution, z, viewScale) : 'Arial'
75
- const fontWeight = this.fontWeight ? this.fontWeight(cell, resolution, z, viewScale) : 'bold'
76
- ctx.font = fontWeight + ' ' + fontSizePix + 'px ' + fontFamily
77
-
78
- //get offset
79
- const offset = this.offset(cell, resolution, z)
80
-
81
- //text position
82
- ctx.textAlign = 'center'
83
- const tx = geoCanvas.geoToPixX(cell.x + resolution * 0.5 + offset.dx)
84
- const ty = geoCanvas.geoToPixY(cell.y + resolution * 0.5 + offset.dy) + fontSizePix * 0.3 //it should be 0.5 but 0.3 seems to work better
85
-
86
- //draw the text
87
- ctx.fillText(text, tx, ty)
88
- }
89
-
90
- //update legends
91
- this.updateLegends({ style: this, resolution: resolution, z: z, viewScale: viewScale })
92
- }
93
-
94
- /**
95
- * Build a function [0,1]->string for characters legend
96
- *
97
- * @param {Array.<string>} chars
98
- * @param {(function(number):number)|undefined} scale
99
- * @returns {function(number):string}
100
- */
101
- static textScale(chars, scale = undefined) {
102
- const nb = chars.length
103
- return (t) => {
104
- if (scale) t = scale(t)
105
- if (t == 0) return ''
106
- if (t >= 1) return chars[nb - 1]
107
- return chars[Math.floor(t * nb)]
108
- }
109
- }
110
- }
1
+ //@ts-check
2
+ 'use strict'
3
+
4
+ import { Style } from '../core/Style.js'
5
+
6
+ /**
7
+ * @module style
8
+ * @author Julien Gaffuri
9
+ */
10
+ export class TextStyle extends Style {
11
+ /** @param {object} opts */
12
+ constructor(opts) {
13
+ super(opts)
14
+ opts = opts || {}
15
+
16
+ /** A function returning the text of a cell.
17
+ * @type {function(import('../core/Dataset.js').Cell, number, number, object):string} */
18
+ this.text = opts.text || (() => 'X') //(c,r,z,vs) => {}
19
+
20
+ /** A function returning the color of the cell.
21
+ * @type {function(import('../core/Dataset.js').Cell, number, number, object):string} */
22
+ this.color = opts.color || (() => 'black') //(c,r,z,vs) => {}
23
+
24
+ /** A function returning the font size of a cell in geo unit.
25
+ * @type {function(import('../core/Dataset.js').Cell, number, number,object):number} */
26
+ this.fontSize = opts.fontSize || ((cell, resolution) => resolution) //(c,r,z,vs) => {}
27
+
28
+ /** The text font family.
29
+ * @type {function(import('../core/Dataset.js').Cell, number, number, object):string} */
30
+ this.fontFamily = opts.fontFamily || (() => 'Arial')
31
+
32
+ /** The text font weight.
33
+ * @type {function(import('../core/Dataset.js').Cell, number, number, object):string} */
34
+ this.fontWeight = opts.fontWeight || (() => 'bold')
35
+ }
36
+
37
+ /**
38
+ * Draw cells as text.
39
+ *
40
+ * @param {Array.<import("../core/Dataset").Cell>} cells
41
+ * @param {import("../core/GeoCanvas").GeoCanvas} geoCanvas
42
+ * @param {number} resolution
43
+ */
44
+ draw(cells, geoCanvas, resolution) {
45
+ //filter
46
+ if (this.filter) cells = cells.filter(this.filter)
47
+
48
+ //
49
+ const z = geoCanvas.view.z
50
+ const ctx = geoCanvas.offscreenCtx
51
+ //get view scale
52
+ const viewScale = this.viewScale ? this.viewScale(cells, resolution, z) : undefined
53
+
54
+ //draw with HTML canvas
55
+ //in screen coordinates
56
+ geoCanvas.initCanvasTransform()
57
+
58
+ for (let cell of cells) {
59
+ //get cell text
60
+ const text = this.text ? this.text(cell, resolution, z, viewScale) : undefined
61
+ if (text == undefined || text == null || text + '' === '') continue
62
+
63
+ //color
64
+ const col = this.color ? this.color(cell, resolution, z, viewScale) : undefined
65
+ if (!col) continue
66
+ ctx.fillStyle = col
67
+
68
+ //font size
69
+ //size - in pixel unit
70
+ const fontSizePix = this.fontSize(cell, resolution, z, viewScale) / z
71
+ if (!fontSizePix) continue
72
+
73
+ //set font
74
+ const fontFamily = this.fontFamily ? this.fontFamily(cell, resolution, z, viewScale) : 'Arial'
75
+ const fontWeight = this.fontWeight ? this.fontWeight(cell, resolution, z, viewScale) : 'bold'
76
+ ctx.font = fontWeight + ' ' + fontSizePix + 'px ' + fontFamily
77
+
78
+ //get offset
79
+ const offset = this.offset(cell, resolution, z)
80
+
81
+ //text position
82
+ ctx.textAlign = 'center'
83
+ const tx = geoCanvas.geoToPixX(cell.x + resolution * 0.5 + offset.dx)
84
+ const ty = geoCanvas.geoToPixY(cell.y + resolution * 0.5 + offset.dy) + fontSizePix * 0.3 //it should be 0.5 but 0.3 seems to work better
85
+
86
+ //draw the text
87
+ ctx.fillText(text, tx, ty)
88
+ }
89
+
90
+ //update legends
91
+ this.updateLegends({ style: this, resolution: resolution, z: z, viewScale: viewScale })
92
+ }
93
+
94
+ /**
95
+ * Build a function [0,1]->string for characters legend
96
+ *
97
+ * @param {Array.<string>} chars
98
+ * @param {(function(number):number)|undefined} scale
99
+ * @returns {function(number):string}
100
+ */
101
+ static textScale(chars, scale = undefined) {
102
+ const nb = chars.length
103
+ return (t) => {
104
+ if (scale) t = scale(t)
105
+ if (t == 0) return ''
106
+ if (t >= 1) return chars[nb - 1]
107
+ return chars[Math.floor(t * nb)]
108
+ }
109
+ }
110
+ }
@@ -1,222 +1,222 @@
1
- //@ts-check
2
- 'use strict'
3
-
4
- import { Style } from '../core/Style.js'
5
-
6
- /** @typedef {"first"|"bottom"|"center"|"top"|"last"} AnchorModeYEnum */
7
-
8
- /**
9
- * Show cell as timeseries chart
10
- * Can be used for sparkline map of https://datagistips.hypotheses.org/488
11
- *
12
- * @module style
13
- * @author Julien Gaffuri
14
- */
15
- export class TimeSeriesStyle extends Style {
16
- /** @param {object} opts */
17
- constructor(opts) {
18
- super(opts)
19
- opts = opts || {}
20
-
21
- /** The columns of the time series, ordered in chronological order.
22
- * @type {Array.<string>} */
23
- this.ts = opts.ts
24
-
25
- /** A function specifying when a value should be considered as "no data" and thus not ignored. The line will have a break at these values.
26
- * @type {function(string):boolean} */
27
- this.noData = opts.noData || ((v) => v === undefined || v == '' || v === null || isNaN(+v))
28
-
29
- //x
30
- /** in geo unit
31
- * @type {function(import("../core/Dataset.js").Cell,number,number):number} */
32
- this.offsetX = opts.offsetX || ((c, r, z) => 0)
33
- /** @type {function(import("../core/Dataset.js").Cell,number,number):number} */
34
- this.width = opts.width || ((c, r, z) => r)
35
-
36
- //y
37
- /** in geo unit
38
- * @type {function(import("../core/Dataset.js").Cell,number,number):number} */
39
- this.offsetY = opts.offsetY || ((c, r, z) => 0)
40
- /** @type {function(import("../core/Dataset.js").Cell,number,number):number} */
41
- this.height = opts.height || ((c, r, z) => r)
42
- /** @type {function(import("../core/Dataset.js").Cell,number,number):AnchorModeYEnum} */
43
- this.anchorModeY = opts.anchorModeY || ((c, r, z) => 'center')
44
-
45
- /** A function returning the width of the line, in geo unit
46
- * @type {function(import('../core/Dataset.js').Cell, number, number, object):number} */
47
- this.lineWidth = opts.lineWidth || ((v, r, s, z) => 1.5 * z)
48
-
49
- /** A function returning the color of the chart.
50
- * @type {function(import('../core/Dataset.js').Cell, number, number, object):string} */
51
- this.color = opts.color || (() => 'black') //(c,r,z,vs) => {}
52
- }
53
-
54
- /**
55
- * Draw cells as text.
56
- *
57
- * @param {Array.<import("../core/Dataset.js").Cell>} cells
58
- * @param {import("../core/GeoCanvas.js").GeoCanvas} geoCanvas
59
- * @param {number} resolution
60
- */
61
- draw(cells, geoCanvas, resolution) {
62
- //filter
63
- if (this.filter) cells = cells.filter(this.filter)
64
-
65
- //
66
- const z = geoCanvas.view.z
67
- const ctx = geoCanvas.offscreenCtx
68
-
69
- //get view scale
70
- const viewScale = this.viewScale ? this.viewScale(cells, resolution, z) : undefined
71
-
72
- //compute cell amplitude
73
- const getAmplitude = (c) => {
74
- let min, max
75
- for (let t of this.ts) {
76
- const val = c[t]
77
- if (val == undefined) continue
78
- if (min == undefined || val < min) min = val
79
- if (max == undefined || val > max) max = val
80
- }
81
- if (min == undefined) return undefined
82
- return max - min
83
- }
84
-
85
- //compute max amplitude
86
- let ampMax
87
- for (let c of cells) {
88
- const amp = getAmplitude(c)
89
- if (amp == undefined) continue
90
- if (ampMax == undefined || amp > ampMax) ampMax = amp
91
- }
92
- if (!ampMax) return
93
-
94
- const nb = this.ts.length
95
-
96
- ctx.lineCap = 'butt'
97
- for (let c of cells) {
98
- //line width
99
- /** @type {number|undefined} */
100
- const wG = this.lineWidth ? this.lineWidth(c, resolution, z, viewScale) : undefined
101
- if (!wG || wG < 0) continue
102
-
103
- //line color
104
- /** @type {string|undefined} */
105
- const col = this.color ? this.color(c, resolution, z, viewScale) : undefined
106
- if (!col) continue
107
-
108
- //x
109
- const offX = this.offsetX ? this.offsetX(c, resolution, z) : 0
110
- if (offX == undefined || isNaN(offX)) continue
111
- const w = this.width ? this.width(c, resolution, z) : resolution
112
- if (w == undefined || isNaN(w)) continue
113
-
114
- //y
115
- const offY = this.offsetY ? this.offsetY(c, resolution, z) : 0
116
- if (offY == undefined || isNaN(offY)) continue
117
- const h = this.height ? this.height(c, resolution, z) : resolution
118
- if (h == undefined || isNaN(h)) continue
119
- const anchY = this.anchorModeY ? this.anchorModeY(c, resolution, z) : 'center'
120
- if (!anchY) continue
121
-
122
- ctx.lineWidth = wG
123
- ctx.strokeStyle = col
124
-
125
- //compute anchor Y figures
126
- let val0, y0
127
- if (anchY === 'first') {
128
- //get first value
129
- val0 = c[this.ts[0]]
130
- y0 = 0
131
- } else if (anchY === 'last') {
132
- //get last value
133
- val0 = c[this.ts[this.ts.length - 1]]
134
- y0 = 0
135
- } else if (anchY === 'bottom') {
136
- //get min
137
- for (let t of this.ts) {
138
- const val = +c[t]
139
- if (val == undefined) continue
140
- if (val0 == undefined || val < val0) val0 = val
141
- }
142
- y0 = 0
143
- } else if (anchY === 'top') {
144
- //get max
145
- for (let t of this.ts) {
146
- const val = +c[t]
147
- if (val == undefined) continue
148
- if (val0 == undefined || val > val0) val0 = val
149
- }
150
- y0 = resolution
151
- } else if (anchY === 'center') {
152
- //get min and max
153
- let min, max
154
- for (let t of this.ts) {
155
- const val = c[t]
156
- if (val == undefined) continue
157
- if (min == undefined || val < min) min = val
158
- if (max == undefined || val > max) max = val
159
- }
160
- val0 = (+max + +min) * 0.5
161
- y0 = resolution / 2
162
- } else {
163
- console.log('Unexpected anchorModeY: ' + anchY)
164
- continue
165
- }
166
-
167
- /*/draw line
168
- if (val0 == undefined || isNaN(val0)) continue
169
- cg.ctx.beginPath()
170
- const sX = w / (nb - 1)
171
- for (let i = 0; i < nb; i++) {
172
- const val = c[this.ts[i]]
173
- if (val == undefined || isNaN(val)) break
174
- if (i == 0)
175
- cg.ctx.moveTo(c.x + i * sX + offX, c.y + y0 + (val - val0) * h / ampMax + offY)
176
- else
177
- cg.ctx.lineTo(c.x + i * sX + offX, c.y + y0 + (val - val0) * h / ampMax + offY)
178
- }
179
- cg.ctx.stroke()*/
180
-
181
- //draw line, segment by segment
182
- const sX = w / (nb - 1)
183
-
184
- //handle first point
185
- let v0 = c[this.ts[0]]
186
- if (!this.noData(v0)) {
187
- ctx.beginPath()
188
- ctx.moveTo(c.x + offX, c.y + y0 + ((v0 - val0) * h) / ampMax + offY)
189
- }
190
- //console.log(v0, isNaN(v0))
191
-
192
- let v1
193
- for (let i = 1; i < nb; i++) {
194
- v1 = c[this.ts[i]]
195
-
196
- //draw segment from v0 to v1
197
-
198
- //both points 'no data'
199
- if (this.noData(v0) && this.noData(v1)) {
200
- //second point 'no data'
201
- } else if (!this.noData(v0) && this.noData(v1)) {
202
- ctx.stroke()
203
-
204
- //first point 'no data'
205
- } else if (this.noData(v0) && !this.noData(v1)) {
206
- ctx.beginPath()
207
- ctx.moveTo(c.x + i * sX + offX, c.y + y0 + ((v1 - val0) * h) / ampMax + offY)
208
-
209
- //both points have data: trace line
210
- } else {
211
- ctx.lineTo(c.x + i * sX + offX, c.y + y0 + ((v1 - val0) * h) / ampMax + offY)
212
- //if it is the last point, stroke
213
- if (i == nb - 1) ctx.stroke()
214
- }
215
- v0 = v1
216
- }
217
- }
218
-
219
- //update legend, if any
220
- this.updateLegends({ style: this, resolution: resolution, z: z, viewScale: viewScale })
221
- }
222
- }
1
+ //@ts-check
2
+ 'use strict'
3
+
4
+ import { Style } from '../core/Style.js'
5
+
6
+ /** @typedef {"first"|"bottom"|"center"|"top"|"last"} AnchorModeYEnum */
7
+
8
+ /**
9
+ * Show cell as timeseries chart
10
+ * Can be used for sparkline map of https://datagistips.hypotheses.org/488
11
+ *
12
+ * @module style
13
+ * @author Julien Gaffuri
14
+ */
15
+ export class TimeSeriesStyle extends Style {
16
+ /** @param {object} opts */
17
+ constructor(opts) {
18
+ super(opts)
19
+ opts = opts || {}
20
+
21
+ /** The columns of the time series, ordered in chronological order.
22
+ * @type {Array.<string>} */
23
+ this.ts = opts.ts
24
+
25
+ /** A function specifying when a value should be considered as "no data" and thus not ignored. The line will have a break at these values.
26
+ * @type {function(string):boolean} */
27
+ this.noData = opts.noData || ((v) => v === undefined || v == '' || v === null || isNaN(+v))
28
+
29
+ //x
30
+ /** in geo unit
31
+ * @type {function(import("../core/Dataset.js").Cell,number,number):number} */
32
+ this.offsetX = opts.offsetX || ((c, r, z) => 0)
33
+ /** @type {function(import("../core/Dataset.js").Cell,number,number):number} */
34
+ this.width = opts.width || ((c, r, z) => r)
35
+
36
+ //y
37
+ /** in geo unit
38
+ * @type {function(import("../core/Dataset.js").Cell,number,number):number} */
39
+ this.offsetY = opts.offsetY || ((c, r, z) => 0)
40
+ /** @type {function(import("../core/Dataset.js").Cell,number,number):number} */
41
+ this.height = opts.height || ((c, r, z) => r)
42
+ /** @type {function(import("../core/Dataset.js").Cell,number,number):AnchorModeYEnum} */
43
+ this.anchorModeY = opts.anchorModeY || ((c, r, z) => 'center')
44
+
45
+ /** A function returning the width of the line, in geo unit
46
+ * @type {function(import('../core/Dataset.js').Cell, number, number, object):number} */
47
+ this.lineWidth = opts.lineWidth || ((v, r, s, z) => 1.5 * z)
48
+
49
+ /** A function returning the color of the chart.
50
+ * @type {function(import('../core/Dataset.js').Cell, number, number, object):string} */
51
+ this.color = opts.color || (() => 'black') //(c,r,z,vs) => {}
52
+ }
53
+
54
+ /**
55
+ * Draw cells as text.
56
+ *
57
+ * @param {Array.<import("../core/Dataset.js").Cell>} cells
58
+ * @param {import("../core/GeoCanvas.js").GeoCanvas} geoCanvas
59
+ * @param {number} resolution
60
+ */
61
+ draw(cells, geoCanvas, resolution) {
62
+ //filter
63
+ if (this.filter) cells = cells.filter(this.filter)
64
+
65
+ //
66
+ const z = geoCanvas.view.z
67
+ const ctx = geoCanvas.offscreenCtx
68
+
69
+ //get view scale
70
+ const viewScale = this.viewScale ? this.viewScale(cells, resolution, z) : undefined
71
+
72
+ //compute cell amplitude
73
+ const getAmplitude = (c) => {
74
+ let min, max
75
+ for (let t of this.ts) {
76
+ const val = c[t]
77
+ if (val == undefined) continue
78
+ if (min == undefined || val < min) min = val
79
+ if (max == undefined || val > max) max = val
80
+ }
81
+ if (min == undefined) return undefined
82
+ return max - min
83
+ }
84
+
85
+ //compute max amplitude
86
+ let ampMax
87
+ for (let c of cells) {
88
+ const amp = getAmplitude(c)
89
+ if (amp == undefined) continue
90
+ if (ampMax == undefined || amp > ampMax) ampMax = amp
91
+ }
92
+ if (!ampMax) return
93
+
94
+ const nb = this.ts.length
95
+
96
+ ctx.lineCap = 'butt'
97
+ for (let c of cells) {
98
+ //line width
99
+ /** @type {number|undefined} */
100
+ const wG = this.lineWidth ? this.lineWidth(c, resolution, z, viewScale) : undefined
101
+ if (!wG || wG < 0) continue
102
+
103
+ //line color
104
+ /** @type {string|undefined} */
105
+ const col = this.color ? this.color(c, resolution, z, viewScale) : undefined
106
+ if (!col) continue
107
+
108
+ //x
109
+ const offX = this.offsetX ? this.offsetX(c, resolution, z) : 0
110
+ if (offX == undefined || isNaN(offX)) continue
111
+ const w = this.width ? this.width(c, resolution, z) : resolution
112
+ if (w == undefined || isNaN(w)) continue
113
+
114
+ //y
115
+ const offY = this.offsetY ? this.offsetY(c, resolution, z) : 0
116
+ if (offY == undefined || isNaN(offY)) continue
117
+ const h = this.height ? this.height(c, resolution, z) : resolution
118
+ if (h == undefined || isNaN(h)) continue
119
+ const anchY = this.anchorModeY ? this.anchorModeY(c, resolution, z) : 'center'
120
+ if (!anchY) continue
121
+
122
+ ctx.lineWidth = wG
123
+ ctx.strokeStyle = col
124
+
125
+ //compute anchor Y figures
126
+ let val0, y0
127
+ if (anchY === 'first') {
128
+ //get first value
129
+ val0 = c[this.ts[0]]
130
+ y0 = 0
131
+ } else if (anchY === 'last') {
132
+ //get last value
133
+ val0 = c[this.ts[this.ts.length - 1]]
134
+ y0 = 0
135
+ } else if (anchY === 'bottom') {
136
+ //get min
137
+ for (let t of this.ts) {
138
+ const val = +c[t]
139
+ if (val == undefined) continue
140
+ if (val0 == undefined || val < val0) val0 = val
141
+ }
142
+ y0 = 0
143
+ } else if (anchY === 'top') {
144
+ //get max
145
+ for (let t of this.ts) {
146
+ const val = +c[t]
147
+ if (val == undefined) continue
148
+ if (val0 == undefined || val > val0) val0 = val
149
+ }
150
+ y0 = resolution
151
+ } else if (anchY === 'center') {
152
+ //get min and max
153
+ let min, max
154
+ for (let t of this.ts) {
155
+ const val = c[t]
156
+ if (val == undefined) continue
157
+ if (min == undefined || val < min) min = val
158
+ if (max == undefined || val > max) max = val
159
+ }
160
+ val0 = (+max + +min) * 0.5
161
+ y0 = resolution / 2
162
+ } else {
163
+ console.log('Unexpected anchorModeY: ' + anchY)
164
+ continue
165
+ }
166
+
167
+ /*/draw line
168
+ if (val0 == undefined || isNaN(val0)) continue
169
+ cg.ctx.beginPath()
170
+ const sX = w / (nb - 1)
171
+ for (let i = 0; i < nb; i++) {
172
+ const val = c[this.ts[i]]
173
+ if (val == undefined || isNaN(val)) break
174
+ if (i == 0)
175
+ cg.ctx.moveTo(c.x + i * sX + offX, c.y + y0 + (val - val0) * h / ampMax + offY)
176
+ else
177
+ cg.ctx.lineTo(c.x + i * sX + offX, c.y + y0 + (val - val0) * h / ampMax + offY)
178
+ }
179
+ cg.ctx.stroke()*/
180
+
181
+ //draw line, segment by segment
182
+ const sX = w / (nb - 1)
183
+
184
+ //handle first point
185
+ let v0 = c[this.ts[0]]
186
+ if (!this.noData(v0)) {
187
+ ctx.beginPath()
188
+ ctx.moveTo(c.x + offX, c.y + y0 + ((v0 - val0) * h) / ampMax + offY)
189
+ }
190
+ //console.log(v0, isNaN(v0))
191
+
192
+ let v1
193
+ for (let i = 1; i < nb; i++) {
194
+ v1 = c[this.ts[i]]
195
+
196
+ //draw segment from v0 to v1
197
+
198
+ //both points 'no data'
199
+ if (this.noData(v0) && this.noData(v1)) {
200
+ //second point 'no data'
201
+ } else if (!this.noData(v0) && this.noData(v1)) {
202
+ ctx.stroke()
203
+
204
+ //first point 'no data'
205
+ } else if (this.noData(v0) && !this.noData(v1)) {
206
+ ctx.beginPath()
207
+ ctx.moveTo(c.x + i * sX + offX, c.y + y0 + ((v1 - val0) * h) / ampMax + offY)
208
+
209
+ //both points have data: trace line
210
+ } else {
211
+ ctx.lineTo(c.x + i * sX + offX, c.y + y0 + ((v1 - val0) * h) / ampMax + offY)
212
+ //if it is the last point, stroke
213
+ if (i == nb - 1) ctx.stroke()
214
+ }
215
+ v0 = v1
216
+ }
217
+ }
218
+
219
+ //update legend, if any
220
+ this.updateLegends({ style: this, resolution: resolution, z: z, viewScale: viewScale })
221
+ }
222
+ }