gridviz 3.0.15 → 3.0.16

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 (55) hide show
  1. package/README.md +127 -128
  2. package/dist/gridviz.js +7959 -7861
  3. package/dist/gridviz.min.js +1 -1
  4. package/package.json +74 -74
  5. package/src/button/ZoomButtons.js +101 -96
  6. package/src/core/Dataset.js +154 -157
  7. package/src/core/Drawable.js +60 -61
  8. package/src/core/GeoCanvas.js +409 -370
  9. package/src/core/Layer.js +23 -25
  10. package/src/core/Legend.js +108 -110
  11. package/src/core/Map.js +495 -495
  12. package/src/core/MultiResolutionDataset.js +93 -97
  13. package/src/core/Style.js +98 -99
  14. package/src/dataset/CSVGrid.js +112 -112
  15. package/src/dataset/GeoTIFF.js +122 -123
  16. package/src/dataset/JSGrid.js +55 -54
  17. package/src/dataset/TiledGrid.js +289 -288
  18. package/src/dataset/worker.js +18 -0
  19. package/src/index.js +77 -71
  20. package/src/layer/BackgroundLayer.js +162 -158
  21. package/src/layer/BackgroundLayerImage.js +81 -84
  22. package/src/layer/BackgroundLayerWMS.js +104 -108
  23. package/src/layer/GeoJSONLayer.js +214 -220
  24. package/src/layer/GridLayer.js +148 -157
  25. package/src/layer/LabelLayer.js +194 -193
  26. package/src/legend/ColorCategoryLegend.js +109 -110
  27. package/src/legend/ColorDiscreteLegend.js +100 -102
  28. package/src/legend/ColorLegend.js +115 -118
  29. package/src/legend/OrientationLegend.js +89 -91
  30. package/src/legend/SizeLegend.js +224 -224
  31. package/src/legend/TernaryLegend.js +302 -210
  32. package/src/style/CompositionStyle.js +291 -288
  33. package/src/style/DotDensityStyle.js +133 -134
  34. package/src/style/ImageStyle.js +100 -102
  35. package/src/style/IsoFenceStyle.js +217 -203
  36. package/src/style/JoyPlotStyle.js +127 -128
  37. package/src/style/LegoStyle.js +110 -110
  38. package/src/style/MosaicStyle.js +99 -99
  39. package/src/style/NinjaStarStyle.js +102 -102
  40. package/src/style/PillarStyle.js +207 -208
  41. package/src/style/SegmentStyle.js +105 -105
  42. package/src/style/ShapeColorSizeStyle.js +147 -132
  43. package/src/style/SideCategoryStyle.js +112 -114
  44. package/src/style/SideStyle.js +212 -205
  45. package/src/style/SquareColorCategoryWebGLStyle.js +107 -108
  46. package/src/style/SquareColorWebGLStyle.js +126 -126
  47. package/src/style/StrokeStyle.js +135 -136
  48. package/src/style/TanakaStyle.js +69 -68
  49. package/src/style/TextStyle.js +110 -111
  50. package/src/style/TimeSeriesStyle.js +222 -229
  51. package/src/utils/scale.js +218 -228
  52. package/src/utils/stretching.js +120 -133
  53. package/src/utils/ternary.js +203 -204
  54. package/src/utils/utils.js +58 -63
  55. package/src/utils/webGLUtils.js +72 -70
@@ -1,126 +1,126 @@
1
- //@ts-check
2
- 'use strict'
3
-
4
- import { Style } from '../core/Style.js'
5
- import { makeWebGLCanvas } from '../utils/webGLUtils.js'
6
- import { WebGLSquareColoringAdvanced } from '../utils/WebGLSquareColoringAdvanced.js'
7
-
8
- /**
9
- * Style based on webGL
10
- * To show cells as colored squares, with computation of the colors on GPU side (faster than JavaScript side).
11
- * Alls squares with the same size
12
- *
13
- * @module style
14
- * @author Julien Gaffuri
15
- */
16
- export class SquareColorWebGLStyle extends Style {
17
- /** @param {object} opts */
18
- constructor(opts) {
19
- super(opts)
20
- opts = opts || {}
21
-
22
- /**
23
- * A function returning a t value (within [0,1]) for a cell.
24
- * @type {function(import('../core/Dataset.js').Cell,number,number,object):number} */
25
- this.tFun = opts.tFun //(c,r,z,vs) => {}
26
-
27
- /**
28
- * Distribution stretching method.
29
- * The stretching is performed on GPU side (fragment shader).
30
- * @type {{ fun:string, alpha:number }} */
31
- this.stretching = opts.stretching
32
-
33
- /**
34
- * The sample of the color ramp.
35
- * The color is computed on GPU side (fragment shader) based on those values (linear interpolation).
36
- * @type {Array.<string>} */
37
- this.colors =
38
- opts.colors ||
39
- [
40
- 'rgb(158, 1, 66)',
41
- 'rgb(248, 142, 83)',
42
- 'rgb(251, 248, 176)',
43
- 'rgb(137, 207, 165)',
44
- 'rgb(94, 79, 162)',
45
- ].reverse()
46
- if (opts.color)
47
- this.colors = [
48
- opts.color(0),
49
- opts.color(0.2),
50
- opts.color(0.4),
51
- opts.color(0.6),
52
- opts.color(0.8),
53
- opts.color(1),
54
- ]
55
-
56
- /**
57
- * Define the opacity of the style, within [0,1].
58
- * If this opacity is defined, the individual color opacity will be ignored.
59
- * @type {function(number,number):number} */
60
- this.opacity = opts.opacity // (r,z) => ...
61
-
62
- /**
63
- * A function returning the size of the cells, in geographical unit. All cells have the same size.
64
- * @type {function(number,number):number} */
65
- this.size = opts.size // (resolution, z) => ...
66
- }
67
-
68
- /**
69
- * @param {Array.<import("../core/Dataset.js").Cell>} cells
70
- * @param {import("../core/GeoCanvas.js").GeoCanvas} geoCanvas
71
- * @param {number} resolution
72
- */
73
- draw(cells, geoCanvas, resolution) {
74
-
75
- //filter
76
- if (this.filter) cells = cells.filter(this.filter)
77
-
78
- //
79
- const z = geoCanvas.view.z
80
-
81
- //get view scale
82
- const viewScale = this.viewScale ? this.viewScale(cells, resolution, z) : undefined
83
-
84
- //create canvas and webgl renderer
85
- //for opacity control, see: https://webglfundamentals.org/webgl/lessons/webgl-and-alpha.html
86
- const cvWGL = makeWebGLCanvas(
87
- geoCanvas.w + '',
88
- geoCanvas.h + '',
89
- this.opacity != undefined ? { premultipliedAlpha: false } : undefined
90
- )
91
- if (!cvWGL) {
92
- console.error('No webGL')
93
- return
94
- }
95
-
96
- //add vertice and fragment data
97
- const r2 = resolution / 2
98
- const verticesBuffer = []
99
- const tBuffer = []
100
- for (let cell of cells) {
101
- const t = this.tFun(cell, resolution, z, viewScale)
102
- if (t == null || t == undefined) continue
103
- verticesBuffer.push(cell.x + r2, cell.y + r2)
104
- tBuffer.push(t > 1 ? 1 : t < 0 ? 0 : t)
105
- }
106
-
107
- //compute pixel size
108
- const sizeGeo = this.size ? this.size(resolution, z) : resolution + 0.2 * z
109
-
110
- //compute opacity
111
- const op = this.opacity ? this.opacity(resolution, z) : undefined
112
-
113
- //
114
- const wgp = new WebGLSquareColoringAdvanced(cvWGL.gl, this.colors, this.stretching, sizeGeo / z, op)
115
-
116
- //draw
117
- wgp.draw(verticesBuffer, tBuffer, geoCanvas.getWebGLTransform())
118
-
119
- //draw in canvas geo
120
- geoCanvas.initCanvasTransform()
121
- geoCanvas.ctx.drawImage(cvWGL.canvas, 0, 0)
122
-
123
- //update legends
124
- this.updateLegends({ style: this, resolution: resolution, z: z, viewScale: viewScale })
125
- }
126
- }
1
+ //@ts-check
2
+ 'use strict'
3
+
4
+ import { Style } from '../core/Style.js'
5
+ import { makeWebGLCanvas } from '../utils/webGLUtils.js'
6
+ import { WebGLSquareColoringAdvanced } from '../utils/WebGLSquareColoringAdvanced.js'
7
+
8
+ /**
9
+ * Style based on webGL
10
+ * To show cells as colored squares, with computation of the colors on GPU side (faster than JavaScript side).
11
+ * Alls squares with the same size
12
+ *
13
+ * @module style
14
+ * @author Julien Gaffuri
15
+ */
16
+ export class SquareColorWebGLStyle extends Style {
17
+ /** @param {object} opts */
18
+ constructor(opts) {
19
+ super(opts)
20
+ opts = opts || {}
21
+
22
+ /**
23
+ * A function returning a t value (within [0,1]) for a cell.
24
+ * @type {function(import('../core/Dataset.js').Cell,number,number,object):number} */
25
+ this.tFun = opts.tFun //(c,r,z,vs) => {}
26
+
27
+ /**
28
+ * Distribution stretching method.
29
+ * The stretching is performed on GPU side (fragment shader).
30
+ * @type {{ fun:string, alpha:number }} */
31
+ this.stretching = opts.stretching
32
+
33
+ /**
34
+ * The sample of the color ramp.
35
+ * The color is computed on GPU side (fragment shader) based on those values (linear interpolation).
36
+ * @type {Array.<string>} */
37
+ this.colors =
38
+ opts.colors ||
39
+ [
40
+ 'rgb(158, 1, 66)',
41
+ 'rgb(248, 142, 83)',
42
+ 'rgb(251, 248, 176)',
43
+ 'rgb(137, 207, 165)',
44
+ 'rgb(94, 79, 162)',
45
+ ].reverse()
46
+ if (opts.color)
47
+ this.colors = [
48
+ opts.color(0),
49
+ opts.color(0.2),
50
+ opts.color(0.4),
51
+ opts.color(0.6),
52
+ opts.color(0.8),
53
+ opts.color(1),
54
+ ]
55
+
56
+ /**
57
+ * Define the opacity of the style, within [0,1].
58
+ * If this opacity is defined, the individual color opacity will be ignored.
59
+ * @type {function(number,number):number} */
60
+ this.opacity = opts.opacity // (r,z) => ...
61
+
62
+ /**
63
+ * A function returning the size of the cells, in geographical unit. All cells have the same size.
64
+ * @type {function(number,number):number} */
65
+ this.size = opts.size // (resolution, z) => ...
66
+ }
67
+
68
+ /**
69
+ * @param {Array.<import("../core/Dataset.js").Cell>} cells
70
+ * @param {import("../core/GeoCanvas.js").GeoCanvas} geoCanvas
71
+ * @param {number} resolution
72
+ */
73
+ draw(cells, geoCanvas, resolution) {
74
+ //filter
75
+ if (this.filter) cells = cells.filter(this.filter)
76
+
77
+ //
78
+ const z = geoCanvas.view.z
79
+
80
+ //get view scale
81
+ const viewScale = this.viewScale ? this.viewScale(cells, resolution, z) : undefined
82
+
83
+ //create canvas and webgl renderer
84
+ //for opacity control, see: https://webglfundamentals.org/webgl/lessons/webgl-and-alpha.html
85
+ const cvWGL = makeWebGLCanvas(
86
+ geoCanvas.w + '',
87
+ geoCanvas.h + '',
88
+ this.opacity != undefined ? { premultipliedAlpha: false } : undefined
89
+ )
90
+ if (!cvWGL) {
91
+ console.error('No webGL')
92
+ return
93
+ }
94
+
95
+ //add vertice and fragment data
96
+ const r2 = resolution / 2
97
+ const verticesBuffer = []
98
+ const tBuffer = []
99
+ for (let cell of cells) {
100
+ const t = this.tFun(cell, resolution, z, viewScale)
101
+ if (t == null || t == undefined) continue
102
+ verticesBuffer.push(cell.x + r2, cell.y + r2)
103
+ tBuffer.push(t > 1 ? 1 : t < 0 ? 0 : t)
104
+ }
105
+
106
+ //compute pixel size
107
+ const sizeGeo = this.size ? this.size(resolution, z) : resolution + 0.2 * z
108
+
109
+ //compute opacity
110
+ const op = this.opacity ? this.opacity(resolution, z) : undefined
111
+
112
+ //
113
+ const wgp = new WebGLSquareColoringAdvanced(cvWGL.gl, this.colors, this.stretching, sizeGeo / z, op)
114
+
115
+ //draw
116
+ wgp.draw(verticesBuffer, tBuffer, geoCanvas.getWebGLTransform())
117
+
118
+ // draw in canvas geo
119
+ // NOTE: drawing each tile this way is very inefficient. WebGL is best used with fewer, heavier/larger draw calls.
120
+ geoCanvas.initCanvasTransform()
121
+ geoCanvas.offscreenCtx.drawImage(cvWGL.canvas, 0, 0)
122
+
123
+ //update legends
124
+ this.updateLegends({ style: this, resolution: resolution, z: z, viewScale: viewScale })
125
+ }
126
+ }
@@ -1,136 +1,135 @@
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 StrokeStyle extends Style {
11
- /** @param {object} opts */
12
- constructor(opts) {
13
- super(opts)
14
- opts = opts || {}
15
-
16
- /** A function returning the color of the cell.
17
- * @type {function(import('../core/Dataset.js').Cell,number,number,object):string} */
18
- this.strokeColor = opts.strokeColor || (() => "#666") //(c,r,z,vs) => {}
19
-
20
- /** A function returning the size of a cell in geographical unit.
21
- * @type {function(import('../core/Dataset.js').Cell,number,number,object):number} */
22
- this.size = opts.size || ((cell, resolution) => resolution) //(c,r,z,vs) => {}
23
-
24
- /** The stroke line width in geographical unit.
25
- * @type {function(import('../core/Dataset.js').Cell,number,number,object):number} */
26
- this.strokeWidth = opts.strokeWidth || ((cell, resolution, z) => z * 1.5) //(c,r,z,vs) => {}
27
-
28
- /** A function returning the shape of a cell.
29
- * @type {function(import("../core/Dataset.js").Cell,number,number,object):import("../core/Style.js").Shape} */
30
- this.shape = opts.shape || (() => "square") //(c,r,z,vs) => {}
31
- }
32
-
33
- /**
34
- * Draw cells as squares, with various colors and size.
35
- *
36
- * @param {Array.<import("../core/Dataset").Cell>} cells
37
- * @param {import("../core/GeoCanvas").GeoCanvas} geoCanvas
38
- * @param {number} resolution
39
- */
40
- draw(cells, geoCanvas, resolution) {
41
-
42
- //filter
43
- if (this.filter) cells = cells.filter(this.filter)
44
-
45
- //
46
- const z = geoCanvas.view.z
47
-
48
- //get view scale
49
- const viewScale = this.viewScale ? this.viewScale(cells, resolution, z) : undefined
50
-
51
- const r2 = resolution * 0.5
52
- for (let c of cells) {
53
-
54
- //color
55
- const col = this.strokeColor ? this.strokeColor(c, resolution, z, viewScale) : undefined
56
- if (!col || col === 'none') continue
57
- geoCanvas.ctx.strokeStyle = col
58
-
59
- //size - in geo unit
60
- const size = this.size ? this.size(c, resolution, z, viewScale) : resolution
61
-
62
- //width
63
- const wi = this.strokeWidth ? this.strokeWidth(c, resolution, z, viewScale) : 1 * z
64
- if (!wi || wi <= 0) continue
65
- geoCanvas.ctx.lineWidth = wi
66
-
67
- //shape
68
- const shape = this.shape ? this.shape(c, resolution, z, viewScale) : 'square'
69
- if (shape === 'none') continue
70
-
71
- //get offset
72
- const offset = this.offset(c, resolution, z)
73
-
74
- if (shape === 'square') {
75
- //draw square
76
- const d = resolution * (1 - size / resolution) * 0.5
77
- geoCanvas.ctx.beginPath()
78
- geoCanvas.ctx.rect(c.x + d + offset.dx, c.y + d + offset.dy, size, size)
79
- geoCanvas.ctx.stroke()
80
- } else if (shape === 'circle') {
81
- //draw circle
82
- geoCanvas.ctx.beginPath()
83
- geoCanvas.ctx.arc(c.x + r2 + offset.dx, c.y + r2 + offset.dy, size * 0.5, 0, 2 * Math.PI, false)
84
- geoCanvas.ctx.stroke()
85
- } else if (shape === 'diamond') {
86
- const s2 = size * 0.5
87
- geoCanvas.ctx.beginPath()
88
- geoCanvas.ctx.moveTo(c.x + r2 - s2, c.y + r2)
89
- geoCanvas.ctx.lineTo(c.x + r2, c.y + r2 + s2)
90
- geoCanvas.ctx.lineTo(c.x + r2 + s2, c.y + r2)
91
- geoCanvas.ctx.lineTo(c.x + r2, c.y + r2 - s2)
92
- geoCanvas.ctx.lineTo(c.x + r2 - s2, c.y + r2)
93
- geoCanvas.ctx.stroke()
94
- } else if (shape === 'donut') {
95
- console.error('Not implemented')
96
- } else if (shape === 'triangle_up') {
97
- const dr2 = (size - resolution) / 2
98
- geoCanvas.ctx.beginPath()
99
- geoCanvas.ctx.moveTo(c.x - dr2, c.y - dr2)
100
- geoCanvas.ctx.lineTo(c.x + r2, c.y + resolution + dr2)
101
- geoCanvas.ctx.lineTo(c.x + resolution + dr2, c.y - dr2)
102
- geoCanvas.ctx.closePath()
103
- geoCanvas.ctx.stroke()
104
- } else if (shape === 'triangle_down') {
105
- const dr2 = (size - resolution) / 2
106
- geoCanvas.ctx.beginPath()
107
- geoCanvas.ctx.moveTo(c.x - dr2, c.y + resolution + dr2)
108
- geoCanvas.ctx.lineTo(c.x + r2, c.y - dr2)
109
- geoCanvas.ctx.lineTo(c.x + resolution + dr2, c.y + resolution + dr2)
110
- geoCanvas.ctx.closePath()
111
- geoCanvas.ctx.stroke()
112
- } else if (shape === 'triangle_left') {
113
- const dr2 = (size - resolution) / 2
114
- geoCanvas.ctx.beginPath()
115
- geoCanvas.ctx.moveTo(c.x + resolution + dr2, c.y + resolution + dr2)
116
- geoCanvas.ctx.lineTo(c.x - dr2, c.y + r2)
117
- geoCanvas.ctx.lineTo(c.x + resolution + dr2, c.y - dr2)
118
- geoCanvas.ctx.closePath()
119
- geoCanvas.ctx.stroke()
120
- } else if (shape === 'triangle_right') {
121
- const dr2 = (size - resolution) / 2
122
- geoCanvas.ctx.beginPath()
123
- geoCanvas.ctx.moveTo(c.x - dr2, c.y - dr2)
124
- geoCanvas.ctx.lineTo(c.x + resolution + dr2, c.y + r2)
125
- geoCanvas.ctx.lineTo(c.x - dr2, c.y + resolution + dr2)
126
- geoCanvas.ctx.closePath()
127
- geoCanvas.ctx.stroke()
128
- } else {
129
- throw new Error('Unexpected shape:' + shape)
130
- }
131
- }
132
-
133
- //update legends
134
- this.updateLegends({ style: this, resolution: resolution, z: z, viewScale: viewScale })
135
- }
136
- }
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 StrokeStyle extends Style {
11
+ /** @param {object} opts */
12
+ constructor(opts) {
13
+ super(opts)
14
+ opts = opts || {}
15
+
16
+ /** A function returning the color of the cell.
17
+ * @type {function(import('../core/Dataset.js').Cell,number,number,object):string} */
18
+ this.strokeColor = opts.strokeColor || (() => '#666') //(c,r,z,vs) => {}
19
+
20
+ /** A function returning the size of a cell in geographical unit.
21
+ * @type {function(import('../core/Dataset.js').Cell,number,number,object):number} */
22
+ this.size = opts.size || ((cell, resolution) => resolution) //(c,r,z,vs) => {}
23
+
24
+ /** The stroke line width in geographical unit.
25
+ * @type {function(import('../core/Dataset.js').Cell,number,number,object):number} */
26
+ this.strokeWidth = opts.strokeWidth || ((cell, resolution, z) => z * 1.5) //(c,r,z,vs) => {}
27
+
28
+ /** A function returning the shape of a cell.
29
+ * @type {function(import("../core/Dataset.js").Cell,number,number,object):import("../core/Style.js").Shape} */
30
+ this.shape = opts.shape || (() => 'square') //(c,r,z,vs) => {}
31
+ }
32
+
33
+ /**
34
+ * Draw cells as squares, with various colors and size.
35
+ *
36
+ * @param {Array.<import("../core/Dataset").Cell>} cells
37
+ * @param {import("../core/GeoCanvas").GeoCanvas} geoCanvas
38
+ * @param {number} resolution
39
+ */
40
+ draw(cells, geoCanvas, resolution) {
41
+ //filter
42
+ if (this.filter) cells = cells.filter(this.filter)
43
+
44
+ //
45
+ const z = geoCanvas.view.z
46
+ const ctx = geoCanvas.offscreenCtx
47
+
48
+ //get view scale
49
+ const viewScale = this.viewScale ? this.viewScale(cells, resolution, z) : undefined
50
+
51
+ const r2 = resolution * 0.5
52
+ for (let c of cells) {
53
+ //color
54
+ const col = this.strokeColor ? this.strokeColor(c, resolution, z, viewScale) : undefined
55
+ if (!col || col === 'none') continue
56
+ ctx.strokeStyle = col
57
+
58
+ //size - in geo unit
59
+ const size = this.size ? this.size(c, resolution, z, viewScale) : resolution
60
+
61
+ //width
62
+ const wi = this.strokeWidth ? this.strokeWidth(c, resolution, z, viewScale) : 1 * z
63
+ if (!wi || wi <= 0) continue
64
+ ctx.lineWidth = wi
65
+
66
+ //shape
67
+ const shape = this.shape ? this.shape(c, resolution, z, viewScale) : 'square'
68
+ if (shape === 'none') continue
69
+
70
+ //get offset
71
+ const offset = this.offset(c, resolution, z)
72
+
73
+ if (shape === 'square') {
74
+ //draw square
75
+ const d = resolution * (1 - size / resolution) * 0.5
76
+ ctx.beginPath()
77
+ ctx.rect(c.x + d + offset.dx, c.y + d + offset.dy, size, size)
78
+ ctx.stroke()
79
+ } else if (shape === 'circle') {
80
+ //draw circle
81
+ ctx.beginPath()
82
+ ctx.arc(c.x + r2 + offset.dx, c.y + r2 + offset.dy, size * 0.5, 0, 2 * Math.PI, false)
83
+ ctx.stroke()
84
+ } else if (shape === 'diamond') {
85
+ const s2 = size * 0.5
86
+ ctx.beginPath()
87
+ ctx.moveTo(c.x + r2 - s2, c.y + r2)
88
+ ctx.lineTo(c.x + r2, c.y + r2 + s2)
89
+ ctx.lineTo(c.x + r2 + s2, c.y + r2)
90
+ ctx.lineTo(c.x + r2, c.y + r2 - s2)
91
+ ctx.lineTo(c.x + r2 - s2, c.y + r2)
92
+ ctx.stroke()
93
+ } else if (shape === 'donut') {
94
+ console.error('Not implemented')
95
+ } else if (shape === 'triangle_up') {
96
+ const dr2 = (size - resolution) / 2
97
+ ctx.beginPath()
98
+ ctx.moveTo(c.x - dr2, c.y - dr2)
99
+ ctx.lineTo(c.x + r2, c.y + resolution + dr2)
100
+ ctx.lineTo(c.x + resolution + dr2, c.y - dr2)
101
+ ctx.closePath()
102
+ ctx.stroke()
103
+ } else if (shape === 'triangle_down') {
104
+ const dr2 = (size - resolution) / 2
105
+ ctx.beginPath()
106
+ ctx.moveTo(c.x - dr2, c.y + resolution + dr2)
107
+ ctx.lineTo(c.x + r2, c.y - dr2)
108
+ ctx.lineTo(c.x + resolution + dr2, c.y + resolution + dr2)
109
+ ctx.closePath()
110
+ ctx.stroke()
111
+ } else if (shape === 'triangle_left') {
112
+ const dr2 = (size - resolution) / 2
113
+ ctx.beginPath()
114
+ ctx.moveTo(c.x + resolution + dr2, c.y + resolution + dr2)
115
+ ctx.lineTo(c.x - dr2, c.y + r2)
116
+ ctx.lineTo(c.x + resolution + dr2, c.y - dr2)
117
+ ctx.closePath()
118
+ ctx.stroke()
119
+ } else if (shape === 'triangle_right') {
120
+ const dr2 = (size - resolution) / 2
121
+ ctx.beginPath()
122
+ ctx.moveTo(c.x - dr2, c.y - dr2)
123
+ ctx.lineTo(c.x + resolution + dr2, c.y + r2)
124
+ ctx.lineTo(c.x - dr2, c.y + resolution + dr2)
125
+ ctx.closePath()
126
+ ctx.stroke()
127
+ } else {
128
+ throw new Error('Unexpected shape:' + shape)
129
+ }
130
+ }
131
+
132
+ //update legends
133
+ this.updateLegends({ style: this, resolution: resolution, z: z, viewScale: viewScale })
134
+ }
135
+ }