gridviz 3.0.18 → 3.0.20

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 (56) hide show
  1. package/README.md +128 -127
  2. package/dist/gridviz.js +7862 -7819
  3. package/dist/gridviz.min.js +1 -1
  4. package/package.json +74 -74
  5. package/src/button/ZoomButtons.js +101 -101
  6. package/src/core/Dataset.js +154 -154
  7. package/src/core/Drawable.js +60 -60
  8. package/src/core/GeoCanvas.js +430 -419
  9. package/src/core/Layer.js +23 -23
  10. package/src/core/Legend.js +108 -108
  11. package/src/core/Map.js +516 -503
  12. package/src/core/MultiResolutionDataset.js +93 -93
  13. package/src/core/Style.js +98 -98
  14. package/src/core/Tooltip.js +1 -0
  15. package/src/dataset/CSVGrid.js +112 -112
  16. package/src/dataset/GeoTIFF.js +122 -122
  17. package/src/dataset/JSGrid.js +55 -55
  18. package/src/dataset/TiledGrid.js +302 -289
  19. package/src/dataset/worker.js +18 -18
  20. package/src/index.js +77 -77
  21. package/src/layer/BackgroundLayer.js +162 -162
  22. package/src/layer/BackgroundLayerImage.js +81 -81
  23. package/src/layer/BackgroundLayerWMS.js +104 -104
  24. package/src/layer/GeoJSONLayer.js +214 -214
  25. package/src/layer/GridLayer.js +148 -148
  26. package/src/layer/LabelLayer.js +194 -194
  27. package/src/legend/ColorCategoryLegend.js +109 -109
  28. package/src/legend/ColorDiscreteLegend.js +100 -100
  29. package/src/legend/ColorLegend.js +115 -115
  30. package/src/legend/OrientationLegend.js +89 -89
  31. package/src/legend/SizeLegend.js +224 -224
  32. package/src/legend/TernaryLegend.js +302 -302
  33. package/src/style/CompositionStyle.js +291 -291
  34. package/src/style/DotDensityStyle.js +133 -133
  35. package/src/style/ImageStyle.js +100 -100
  36. package/src/style/IsoFenceStyle.js +217 -217
  37. package/src/style/JoyPlotStyle.js +127 -127
  38. package/src/style/LegoStyle.js +110 -110
  39. package/src/style/MosaicStyle.js +99 -99
  40. package/src/style/NinjaStarStyle.js +102 -102
  41. package/src/style/PillarStyle.js +207 -207
  42. package/src/style/SegmentStyle.js +105 -105
  43. package/src/style/ShapeColorSizeStyle.js +147 -147
  44. package/src/style/SideCategoryStyle.js +112 -112
  45. package/src/style/SideStyle.js +212 -212
  46. package/src/style/SquareColorCategoryWebGLStyle.js +107 -107
  47. package/src/style/SquareColorWebGLStyle.js +126 -126
  48. package/src/style/StrokeStyle.js +135 -135
  49. package/src/style/TanakaStyle.js +69 -69
  50. package/src/style/TextStyle.js +110 -110
  51. package/src/style/TimeSeriesStyle.js +222 -222
  52. package/src/utils/scale.js +218 -218
  53. package/src/utils/stretching.js +120 -120
  54. package/src/utils/ternary.js +208 -203
  55. package/src/utils/utils.js +58 -58
  56. package/src/utils/webGLUtils.js +72 -72
@@ -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
+ }