gridviz 3.0.22 → 3.0.24

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 (66) hide show
  1. package/README.md +128 -128
  2. package/dist/gridviz.js +8618 -8579
  3. package/dist/gridviz.min.js +1 -1
  4. package/package.json +74 -74
  5. package/src/button/Button.js +67 -67
  6. package/src/button/FullscreenButton.js +121 -121
  7. package/src/button/ZoomButtons.js +103 -101
  8. package/src/core/Dataset.js +154 -154
  9. package/src/core/Drawable.js +78 -78
  10. package/src/core/GeoCanvas.js +437 -430
  11. package/src/core/Layer.js +23 -23
  12. package/src/core/Legend.js +108 -108
  13. package/src/core/Map.js +542 -516
  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 +77 -77
  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 +214 -214
  28. package/src/layer/GridLayer.js +148 -148
  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/ContourStyle.js +21 -21
  38. package/src/style/DotDensityStyle.js +133 -133
  39. package/src/style/ImageStyle.js +100 -100
  40. package/src/style/IsoFenceStyle.js +217 -217
  41. package/src/style/JoyPlotStyle.js +127 -127
  42. package/src/style/LegoStyle.js +110 -110
  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/ShapeColorSizeStyle.js +147 -147
  48. package/src/style/SideCategoryStyle.js +112 -112
  49. package/src/style/SideStyle.js +212 -212
  50. package/src/style/SquareColorCategoryWebGLStyle.js +107 -107
  51. package/src/style/SquareColorWebGLStyle.js +126 -126
  52. package/src/style/StrokeStyle.js +135 -135
  53. package/src/style/TanakaStyle.js +69 -69
  54. package/src/style/TextStyle.js +110 -110
  55. package/src/style/TimeSeriesStyle.js +222 -222
  56. package/src/utils/WebGLRectangleColoring.js +106 -106
  57. package/src/utils/WebGLSquareColoring.js +118 -118
  58. package/src/utils/WebGLSquareColoringAdvanced.js +230 -230
  59. package/src/utils/WebGLSquareColoringCatAdvanced.js +133 -133
  60. package/src/utils/WebGLSquareColoringKS.js +153 -153
  61. package/src/utils/scale.js +218 -218
  62. package/src/utils/stretching.js +120 -120
  63. package/src/utils/ternary.js +208 -208
  64. package/src/utils/utils.js +58 -58
  65. package/src/utils/webGLUtils.js +72 -72
  66. 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
+ }