gridviz 2.1.17 → 3.0.0

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 +2 -3
  2. package/dist/gridviz.js +4971 -5654
  3. package/dist/gridviz.min.js +1 -1
  4. package/package.json +3 -2
  5. package/src/button/Button.js +14 -15
  6. package/src/button/FullscreenButton.js +14 -14
  7. package/src/button/ZoomButtons.js +2 -2
  8. package/src/{DatasetComponent.js → core/Dataset.js} +33 -12
  9. package/src/core/Drawable.js +60 -0
  10. package/src/{GeoCanvas.js → core/GeoCanvas.js} +65 -155
  11. package/src/core/Layer.js +24 -0
  12. package/src/{Legend.js → core/Legend.js} +24 -1
  13. package/src/core/Map.js +460 -0
  14. package/src/core/MultiResolutionDataset.js +96 -0
  15. package/src/core/Style.js +84 -0
  16. package/src/dataset/COGeo.js +5 -5
  17. package/src/dataset/CSVGrid.js +43 -41
  18. package/src/dataset/GeoTIFF.js +9 -14
  19. package/src/dataset/{LGrid.js → JSGrid.js} +9 -10
  20. package/src/dataset/TiledGrid.js +68 -45
  21. package/src/index.js +22 -29
  22. package/src/{BackgroundLayer.js → layer/BackgroundLayer.js} +27 -52
  23. package/src/{BackgroundLayerWMS.js → layer/BackgroundLayerWMS.js} +21 -45
  24. package/src/{LineLayer.js → layer/GeoJSONLayer.js} +22 -23
  25. package/src/layer/GridLayer.js +140 -0
  26. package/src/{LabelLayer.js → layer/LabelLayer.js} +25 -23
  27. package/src/legend/ColorCategoryLegend.js +9 -24
  28. package/src/legend/ColorDiscreteLegend.js +20 -24
  29. package/src/legend/ColorLegend.js +21 -33
  30. package/src/legend/OrientationLegend.js +90 -0
  31. package/src/legend/SizeLegend.js +153 -72
  32. package/src/style/CompositionStyle.js +71 -84
  33. package/src/style/ContourStyle.js +2 -36
  34. package/src/style/DotDensityStyle.js +50 -73
  35. package/src/style/IsoFenceStyle.js +59 -70
  36. package/src/style/JoyPlotStyle.js +41 -48
  37. package/src/style/LegoStyle.js +4 -4
  38. package/src/style/MosaicStyle.js +30 -39
  39. package/src/style/NinjaStarStyle.js +45 -68
  40. package/src/style/PillarStyle.js +70 -93
  41. package/src/style/SegmentStyle.js +37 -75
  42. package/src/style/ShapeColorSizeStyle.js +49 -98
  43. package/src/style/SideCategoryStyle.js +112 -0
  44. package/src/style/SideStyle.js +99 -133
  45. package/src/style/{SquareColorCatWGLStyle.js → SquareColorCategoryWebGLStyle.js} +28 -41
  46. package/src/style/{SquareColorWGLStyle.js → SquareColorWebGLStyle.js} +28 -49
  47. package/src/style/StrokeStyle.js +43 -70
  48. package/src/style/TanakaStyle.js +34 -132
  49. package/src/style/TextStyle.js +43 -64
  50. package/src/style/TimeSeriesStyle.js +44 -75
  51. package/src/utils/WebGLSquareColoringAdvanced.js +4 -4
  52. package/src/utils/scale.js +202 -0
  53. package/src/utils/stretching.js +86 -129
  54. package/src/utils/{Utils.js → utils.js} +15 -0
  55. package/src/utils/webGLUtils.js +4 -5
  56. package/src/utils/zoomUtils.js +30 -30
  57. package/src/App.js +0 -760
  58. package/src/Dataset.js +0 -76
  59. package/src/Layer.js +0 -103
  60. package/src/Style.js +0 -131
  61. package/src/dataset/GridTile.js +0 -41
  62. package/src/legend/SegmentOrientationLegend.js +0 -87
  63. package/src/legend/SegmentWidthLegend.js +0 -101
  64. package/src/style/CompositionStyle_.js +0 -293
  65. package/src/style/ShapeColorSizeStyle_.js +0 -108
  66. package/src/style/SideCatStyle.js +0 -172
  67. /package/src/{Tooltip.js → core/Tooltip.js} +0 -0
@@ -1,7 +1,7 @@
1
1
  //@ts-check
2
2
  'use strict'
3
3
 
4
- import { Style } from '../Style.js'
4
+ import { Style } from '../core/Style.js'
5
5
 
6
6
  /**
7
7
  *
@@ -13,111 +13,84 @@ export class StrokeStyle extends Style {
13
13
  super(opts)
14
14
  opts = opts || {}
15
15
 
16
- /** The name of the column/attribute of the tabular data where to retrieve the variable for color.
17
- * @type {string} */
18
- this.strokeColorCol = opts.strokeColorCol
19
-
20
- /** A function returning the color of the stroke.
21
- * @type {function(number,number,import("../Style").Stat|undefined):string} */
22
- this.strokeColor = opts.strokeColor || (() => '#666')
23
-
24
- /** The name of the column/attribute of the tabular data where to retrieve the variable for size.
25
- * @type {string} */
26
- this.sizeCol = opts.sizeCol
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) => {}
27
19
 
28
20
  /** A function returning the size of a cell in geographical unit.
29
- * @type {function(number,number,import("../Style").Stat|undefined,number):number} */
30
- this.size = opts.size
31
-
32
- /** The stroke line width, in pixels.
33
- * @type {string} */
34
- this.strokeWidthCol = opts.strokeWidthCol
21
+ * @type {function(import('../core/Dataset.js').Cell,number,number,object):number} */
22
+ this.size = opts.size || ((cell, resolution) => resolution) //(c,r,z,vs) => {}
35
23
 
36
24
  /** The stroke line width in geographical unit.
37
- * @type {function(number,number,import("../Style").Stat|undefined,number):number} */
38
- this.strokeWidth = opts.strokeWidth // (v,r,s,z)=>...
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) => {}
39
27
 
40
28
  /** A function returning the shape of a cell.
41
- * @type {function(import("../Dataset").Cell):import("../Style").Shape} */
42
- this.shape = opts.shape || (() => 'square')
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) => {}
43
31
  }
44
32
 
45
33
  /**
46
34
  * Draw cells as squares, with various colors and size.
47
35
  *
48
- * @param {Array.<import("../Dataset").Cell>} cells
49
- * @param {number} r
50
- * @param {import("../GeoCanvas").GeoCanvas} cg
36
+ * @param {Array.<import("../core/Dataset").Cell>} cells
37
+ * @param {import("../core/GeoCanvas").GeoCanvas} geoCanvas
38
+ * @param {number} resolution
51
39
  */
52
- draw(cells, r, cg) {
40
+ draw(cells, geoCanvas, resolution) {
41
+
53
42
  //filter
54
43
  if (this.filter) cells = cells.filter(this.filter)
55
44
 
56
- //zoom factor
57
- const zf = cg.getZf()
45
+ //
46
+ const z = geoCanvas.view.z
58
47
 
59
- let statColor
60
- if (this.strokeColorCol) statColor = Style.getStatistics(cells, (c) => c[this.strokeColorCol], true)
48
+ //get view scale
49
+ const viewScale = this.viewScale ? this.viewScale(cells, resolution, z) : undefined
61
50
 
62
- let statSize
63
- if (this.sizeCol) statSize = Style.getStatistics(cells, (c) => c[this.sizeCol], true)
64
-
65
- let statWidth
66
- if (this.strokeWidthCol) statWidth = Style.getStatistics(cells, (c) => c[this.strokeWidthCol], true)
67
-
68
- //draw with HTML canvas
69
- //in geo coordinates
70
- cg.setCanvasTransform()
71
-
72
- const r2 = r * 0.5
51
+ const r2 = resolution * 0.5
73
52
  for (let cell of cells) {
53
+
74
54
  //color
75
- const col = this.strokeColor
76
- ? this.strokeColor(cell[this.strokeColorCol], r, statColor)
77
- : undefined
55
+ const col = this.strokeColor ? this.strokeColor(cell, resolution, z, viewScale) : undefined
78
56
  if (!col || col === 'none') continue
79
- cg.ctx.strokeStyle = col
57
+ geoCanvas.ctx.strokeStyle = col
80
58
 
81
- //size
82
- /** @type {function(number,number,import("../Style").Stat|undefined,number):number} */
83
- let s_ = this.size || (() => r)
84
59
  //size - in geo unit
85
- const sG = s_(cell[this.sizeCol], r, statSize, zf)
60
+ const sG = this.size ? this.size(cell, resolution, z, viewScale) : resolution
86
61
 
87
62
  //width
88
- const wi = this.strokeWidth
89
- ? this.strokeWidth(cell[this.strokeWidthCol], r, statWidth, zf)
90
- : 1 * zf
63
+ const wi = this.strokeWidth ? this.strokeWidth(cell, resolution, z, viewScale) : 1 * z
91
64
  if (!wi || wi <= 0) continue
92
- cg.ctx.lineWidth = wi
65
+ geoCanvas.ctx.lineWidth = wi
93
66
 
94
67
  //shape
95
- const shape = this.shape ? this.shape(cell) : 'square'
68
+ const shape = this.shape ? this.shape(cell, resolution, z, viewScale) : 'square'
96
69
  if (shape === 'none') continue
97
70
 
98
71
  //get offset
99
- const offset = this.offset(cell, r, zf)
72
+ const offset = this.offset(cell, resolution, z)
100
73
 
101
74
  if (shape === 'square') {
102
75
  //draw square
103
- const d = r * (1 - sG / r) * 0.5
104
- cg.ctx.beginPath()
105
- cg.ctx.rect(cell.x + d + offset.dx, cell.y + d + offset.dy, sG, sG)
106
- cg.ctx.stroke()
76
+ const d = resolution * (1 - sG / resolution) * 0.5
77
+ geoCanvas.ctx.beginPath()
78
+ geoCanvas.ctx.rect(cell.x + d + offset.dx, cell.y + d + offset.dy, sG, sG)
79
+ geoCanvas.ctx.stroke()
107
80
  } else if (shape === 'circle') {
108
81
  //draw circle
109
- cg.ctx.beginPath()
110
- cg.ctx.arc(cell.x + r2 + offset.dx, cell.y + r2 + offset.dy, sG * 0.5, 0, 2 * Math.PI, false)
111
- cg.ctx.stroke()
82
+ geoCanvas.ctx.beginPath()
83
+ geoCanvas.ctx.arc(cell.x + r2 + offset.dx, cell.y + r2 + offset.dy, sG * 0.5, 0, 2 * Math.PI, false)
84
+ geoCanvas.ctx.stroke()
112
85
  } else if (shape === 'diamond') {
113
86
  const s2 = sG * 0.5
114
- cg.ctx.beginPath()
115
- cg.ctx.moveTo(cell.x + r2 - s2, cell.y + r2)
116
- cg.ctx.lineTo(cell.x + r2, cell.y + r2 + s2)
117
- cg.ctx.lineTo(cell.x + r2 + s2, cell.y + r2)
118
- cg.ctx.lineTo(cell.x + r2, cell.y + r2 - s2)
119
- cg.ctx.lineTo(cell.x + r2 - s2, cell.y + r2)
120
- cg.ctx.stroke()
87
+ geoCanvas.ctx.beginPath()
88
+ geoCanvas.ctx.moveTo(cell.x + r2 - s2, cell.y + r2)
89
+ geoCanvas.ctx.lineTo(cell.x + r2, cell.y + r2 + s2)
90
+ geoCanvas.ctx.lineTo(cell.x + r2 + s2, cell.y + r2)
91
+ geoCanvas.ctx.lineTo(cell.x + r2, cell.y + r2 - s2)
92
+ geoCanvas.ctx.lineTo(cell.x + r2 - s2, cell.y + r2)
93
+ geoCanvas.ctx.stroke()
121
94
  } else if (shape === 'donut') {
122
95
  console.error('Not implemented')
123
96
  } else {
@@ -126,6 +99,6 @@ export class StrokeStyle extends Style {
126
99
  }
127
100
 
128
101
  //update legends
129
- //this.updateLegends({ style: this, r: resolution, zf: zf, sSize: statSize, sColor: statColor });
102
+ this.updateLegends({ style: this, resolution: resolution, z: z, viewScale: viewScale })
130
103
  }
131
104
  }
@@ -1,160 +1,62 @@
1
1
  //@ts-check
2
2
  'use strict'
3
3
 
4
- import { SquareColorWGLStyle } from './SquareColorWGLStyle.js'
5
4
  import { SideStyle } from './SideStyle.js'
5
+ import { SquareColorCategoryWebGLStyle } from './SquareColorCategoryWebGLStyle.js'
6
+ import { classifier as clFun, colorClassifier as cclFun } from '../utils/scale.js'
6
7
 
7
8
  /**
8
- *
9
9
  * @see https://manifold.net/doc/mfd9/example__tanaka_contours.htm
10
10
  *
11
11
  * @author Julien Gaffuri
12
12
  */
13
13
  export class TanakaStyle {
14
14
  /**
15
- * @param {string} col
15
+ * @param {function(import('../core/Dataset.js').Cell):number} value Function that returns the value of a cell
16
+ * @param {Array.<number>} breaks The break values
17
+ * @param {Array.<string>} colors The colors, one more than the break values
16
18
  * @param {object} opts
17
- * @returns {Array.<import("../Style").Style>}
19
+ * @returns {Array.<import("../core/Style").Style>}
18
20
  */
19
- static get(col, opts) {
20
- opts = opts || {}
21
-
22
- //get colors from d3 ramps, if 'nb' is specified
23
- if (opts.nb != undefined) {
24
- if (opts.nb < 2) {
25
- console.error('unexpected number of colors in tanaka (<2): ' + opts.nb)
26
- opts.nb = 2
27
- }
28
- if (!opts.color) {
29
- console.error('color function not defined in tanaka')
30
- opts.color = () => 'gray'
31
- }
32
- opts.colors = []
33
- for (let i = 0; i < opts.nb; i++) opts.colors.push(opts.color(i / (opts.nb - 1)))
34
- }
35
-
36
- /**
37
- * The colors.
38
- * @type {Array.<string>} */
39
- opts.colors = opts.colors || ['#a9bb9e', '#c9dcaa', '#fde89f', '#f9a579', '#eb444b']
40
- const nb = opts.colors.length
41
-
42
- /** A function to compute 't' from the value v
43
- * @type {function(number,number,import("../Style").Stat):number} */
44
- opts.tFun = opts.tFun || ((v, r, s) => (v - s.min) / (s.max - s.min))
21
+ static get(value, breaks, colors, opts = {}) {
45
22
 
46
23
  //shadow colors
47
- opts.colDark = opts.colDark || '#111'
48
- opts.colBright = opts.colBright || '#ddd'
24
+ opts.colorDark = opts.colorDark || '#111'
25
+ opts.colorBright = opts.colorBright || '#ddd'
26
+
27
+ /** @type { function(number, number):number } */
28
+ opts.width = opts.width || ((sideValue, resolution, z) => {
29
+ const minWG = 1 * z
30
+ const maxWG = 4 * z
31
+ const step = (maxWG - minWG) / 3
32
+ return Math.min(minWG + (sideValue - 1) * step, maxWG)
33
+ })
49
34
 
50
- //width of the segment (share of the resolution)
51
- opts.widthFactor = opts.widthFactor || 0.08
35
+ //make classifiers
36
+ const classifier = clFun(breaks)
37
+ const colorClassifier = cclFun(breaks, colors)
52
38
 
53
- //shading
54
- opts.newShading = opts.newShading
55
- opts.newShadingWidthPix = opts.newShadingWidthPix || 2
56
- //transparency value, within [0,1]
57
- opts.newShadingTr =
58
- opts.newShadingTr ||
59
- ((sideValue, sideStat) =>
60
- Math.abs(sideValue) / Math.max(Math.abs(sideStat.min), Math.abs(sideStat.max)))
39
+ const cellStyle = new SquareColorCategoryWebGLStyle({ color: (cell) => colorClassifier(value(cell)) })
61
40
 
62
- /**
63
- * @param {number} t A cell t value, within [0,1].
64
- * @returns the class number for the value
65
- */
66
- const getClass = (t) => {
67
- if (isNaN(t) || t == undefined) {
68
- console.error('Unexpected t value 1: ' + t)
69
- return -9
70
- }
71
- for (let i = 0; i < nb; i++) if (t <= (i + 1) / nb) return i
72
- console.error('Unexpected t value 2: ' + t)
73
- return -9
41
+ const getSideValue = (side) => {
42
+ const cl1 = side.c1 ? classifier(value(side.c1)) : 0
43
+ const cl2 = side.c2 ? classifier(value(side.c2)) : 0
44
+ return cl1 - cl2
74
45
  }
75
46
 
76
- const colStyle = new SquareColorWGLStyle({
77
- colorCol: col,
78
- colors: opts.colors,
79
- tFun: (v, r, s) => {
80
- const t = opts.tFun(v, r, s)
81
- const c = getClass(t)
82
- return c / (nb - 1)
83
- },
84
- //stretching: { fun: "expRev", alpha: -7 },
85
- size: (r, zf) => r + 0.5 * zf, //that is to ensure no gap between same class cells is visible
86
- filter: opts.filter,
87
- })
88
-
89
- /*
90
- if no web gl:
91
- const colStyle = new ShapeColorSizeStyle({
92
- colorCol: col,
93
- //the color corresponding to the class
94
- color: (v, r, s, zf) => {
95
- if (v == 0 && opts.tFun && isNaN(opts.tFun(v, r, s)))
96
- return undefined
97
- return opts.colors[getClass(opts.tFun ? opts.tFun(v, r, s) : v)]
98
- },
99
- shape: () => "square",
100
- size: (v, r, s, zf) => r + 0.5 * zf, //that is to ensure no gap between same class cells is visible
101
- })
102
- */
103
-
104
47
  /** The side style, for the shadow effect */
105
48
  const sideStyle = new SideStyle({
106
- valueCol: col,
107
- value: (v1, v2, r, s, zf) => {
108
- //compute the number of classes of difference
109
- if (v1 === undefined && v2 === undefined) return 0
110
- else if (v2 === undefined) {
111
- const t = opts.tFun(v1, r, s)
112
- if (t == undefined || isNaN(t)) throw new Error('Unexpected value: ' + v1 + ' - ' + t)
113
- const c = getClass(t)
114
- return c + 1
115
- } else if (v1 === undefined) {
116
- const t = opts.tFun(v2, r, s)
117
- if (t == undefined || isNaN(t)) throw new Error('Unexpected value: ' + v2 + ' - ' + t)
118
- const c = getClass(t)
119
- return -c - 1
120
- }
121
- const t1 = opts.tFun(v1, r, s)
122
- if (t1 == undefined || isNaN(t1)) throw new Error('Unexpected value: ' + v1 + ' - ' + t1)
123
- const t2 = opts.tFun(v2, r, s)
124
- if (t2 == undefined || isNaN(t2)) throw new Error('Unexpected value: ' + v2 + ' - ' + t2)
125
- const c1 = getClass(t1)
126
- const c2 = getClass(t2)
127
- return -c2 + c1
49
+ //white or black, depending on orientation and value
50
+ color: (side) => {
51
+ const v = getSideValue(side)
52
+ if (v === 0) return
53
+ if (side.or === 'v') return v < 0 ? opts.colorBright : opts.colorDark
54
+ return v < 0 ? opts.colorDark : opts.colorBright
128
55
  },
129
-
130
- color: opts.newShading
131
- ? //black with transparency depending on difference
132
- (side, r, s, z) => {
133
- const tr = opts.newShadingTr(side.value, s)
134
- return (side.value > 0 && side.or === 'h') || (side.value < 0 && side.or === 'v')
135
- ? 'rgba(255,255,100,' + tr + ')'
136
- : 'rgba(0,0,0,' + tr + ')'
137
- }
138
- : //white or black, depending on orientation and value
139
- (side, r, s, z) => {
140
- if (side.value === 0) return
141
- //return "gray"
142
- if (side.or === 'v') return side.value < 0 ? opts.colBright : opts.colDark
143
- return side.value < 0 ? opts.colDark : opts.colBright
144
- },
145
-
146
- width: opts.newShading
147
- ? //fill size
148
- (side, r, s, z) => {
149
- return opts.newShadingWidthPix * z
150
- }
151
- : //width depends on the value, that is the number of classes of difference
152
- (side, r, s, z) =>
153
- opts.widthFactor * r * Math.abs(side.value) * (side.or === 'v' ? 0.5 : 1),
154
-
155
- filter: opts.filter,
56
+ //width depends on the value, that is the number of classes of difference
57
+ width: (side, resolution, z) => opts.width(Math.abs(getSideValue(side)), resolution, z)
156
58
  })
157
59
 
158
- return [colStyle, sideStyle]
60
+ return [cellStyle, sideStyle]
159
61
  }
160
62
  }
@@ -1,7 +1,7 @@
1
1
  //@ts-check
2
2
  'use strict'
3
3
 
4
- import { Style } from '../Style.js'
4
+ import { Style } from '../core/Style.js'
5
5
 
6
6
  /**
7
7
  *
@@ -13,120 +13,99 @@ export class TextStyle extends Style {
13
13
  super(opts)
14
14
  opts = opts || {}
15
15
 
16
- /** The name of the column/attribute of the tabular data where to retrieve the variable for text.
17
- * @type {string} */
18
- this.textCol = opts.textCol
19
-
20
16
  /** A function returning the text of a cell.
21
- * @type {function(number,number,import("../Style").Stat|undefined,number):string} */
22
- this.text = opts.text || ((v, r, s, z) => 'X')
23
-
24
- /** The name of the column/attribute of the tabular data where to retrieve the variable for color.
25
- * @type {string} */
26
- this.colorCol = opts.colorCol
17
+ * @type {function(import('../core/Dataset.js').Cell, number, number, object):string} */
18
+ this.text = opts.text || (() => 'X') //(c,r,z,vs) => {}
27
19
 
28
20
  /** A function returning the color of the cell.
29
- * @type {function(number,number,import("../Style").Stat|undefined,number):string} */
30
- this.color = opts.color || (() => '#EA6BAC')
31
-
32
- /** The name of the column/attribute of the tabular data where to retrieve the variable for font size.
33
- * @type {string} */
34
- this.fontSizeCol = opts.fontSizeCol
21
+ * @type {function(import('../core/Dataset.js').Cell, number, number, object):string} */
22
+ this.color = opts.color || (() => "black") //(c,r,z,vs) => {}
35
23
 
36
24
  /** A function returning the font size of a cell in geo unit.
37
- * @type {function(number,number,import("../Style").Stat|undefined,number):number} */
38
- this.fontSize = opts.fontSize || ((v, r, s, z) => r * 0.8)
25
+ * @type {function(import('../core/Dataset.js').Cell, number, number,object):number} */
26
+ this.fontSize = opts.fontSize || ((cell, resolution) => resolution) //(c,r,z,vs) => {}
39
27
 
40
28
  /** The text font family.
41
- * @type {string} */
42
- this.fontFamily = opts.fontFamily || 'Arial'
29
+ * @type {function(import('../core/Dataset.js').Cell, number, number, object):string} */
30
+ this.fontFamily = opts.fontFamily || (() => 'Arial')
43
31
 
44
32
  /** The text font weight.
45
- * @type {string} */
46
- this.fontWeight = opts.fontWeight || 'bold'
33
+ * @type {function(import('../core/Dataset.js').Cell, number, number, object):string} */
34
+ this.fontWeight = opts.fontWeight || (() => 'bold')
47
35
  }
48
36
 
49
37
  /**
50
38
  * Draw cells as text.
51
39
  *
52
- * @param {Array.<import("../Dataset").Cell>} cells
53
- * @param {number} r
54
- * @param {import("../GeoCanvas").GeoCanvas} cg
40
+ * @param {Array.<import("../core/Dataset").Cell>} cells
41
+ * @param {import("../core/GeoCanvas").GeoCanvas} geoCanvas
42
+ * @param {number} resolution
55
43
  */
56
- draw(cells, r, cg) {
44
+ draw(cells, geoCanvas, resolution) {
45
+
57
46
  //filter
58
47
  if (this.filter) cells = cells.filter(this.filter)
59
48
 
60
- //zoom factor
61
- const zf = cg.getZf()
62
-
63
- let statText
64
- if (this.textCol) {
65
- //compute text variable statistics
66
- statText = Style.getStatistics(cells, (c) => c[this.textCol], true)
67
- }
49
+ //
50
+ const z = geoCanvas.view.z
68
51
 
69
- let statColor
70
- if (this.colorCol) {
71
- //compute color variable statistics
72
- statColor = Style.getStatistics(cells, (c) => c[this.colorCol], true)
73
- }
74
-
75
- let statFontSize
76
- if (this.fontSizeCol) {
77
- //if size is used, sort cells by size so that the biggest are drawn first
78
- cells.sort((c1, c2) => c2[this.fontSizeCol] - c1[this.fontSizeCol])
79
- //and compute size variable statistics
80
- statFontSize = Style.getStatistics(cells, (c) => c[this.fontSizeCol], true)
81
- }
52
+ //get view scale
53
+ const viewScale = this.viewScale ? this.viewScale(cells, resolution, z) : undefined
82
54
 
83
55
  //draw with HTML canvas
84
56
  //in screen coordinates
85
- cg.initCanvasTransform()
57
+ geoCanvas.initCanvasTransform()
86
58
 
87
59
  for (let cell of cells) {
88
60
  //get cell text
89
- const text = this.text ? this.text(cell[this.textCol], r, statText, zf) : undefined
61
+ const text = this.text ? this.text(cell, resolution, z, viewScale) : undefined
90
62
  if (text == undefined || text == null || text + '' === '') continue
91
63
 
92
64
  //color
93
- const col = this.color ? this.color(cell[this.colorCol], r, statColor, zf) : undefined
65
+ const col = this.color ? this.color(cell, resolution, z, viewScale) : undefined
94
66
  if (!col) continue
95
- cg.ctx.fillStyle = col
67
+ geoCanvas.ctx.fillStyle = col
96
68
 
97
69
  //font size
98
70
  //size - in pixel unit
99
- const fontSizePix = this.fontSize(cell[this.fontSizeCol], r, statFontSize, zf) / zf
71
+ const fontSizePix = this.fontSize(cell, resolution, z, viewScale) / z
72
+ if (!fontSizePix) continue
100
73
 
101
74
  //set font
102
- const fontFamily = this.fontFamily || 'Arial'
103
- const fontWeight = this.fontWeight || 'bold'
104
- cg.ctx.font = fontWeight + ' ' + fontSizePix + 'px ' + fontFamily
75
+ const fontFamily = this.fontFamily ? this.fontFamily(cell, resolution, z, viewScale) : 'Arial'
76
+ const fontWeight = this.fontWeight ? this.fontWeight(cell, resolution, z, viewScale) : 'bold'
77
+ geoCanvas.ctx.font = fontWeight + ' ' + fontSizePix + 'px ' + fontFamily
105
78
 
106
79
  //get offset
107
- const offset = this.offset(cell, r, zf)
80
+ const offset = this.offset(cell, resolution, z)
108
81
 
109
82
  //text position
110
- cg.ctx.textAlign = 'center'
111
- const tx = cg.geoToPixX(cell.x + r * 0.5 + offset.dx)
112
- const ty = cg.geoToPixY(cell.y + r * 0.5 + offset.dy) + fontSizePix * 0.3 //it should be 0.5 but 0.3 seems to work better
83
+ geoCanvas.ctx.textAlign = 'center'
84
+ const tx = geoCanvas.geoToPixX(cell.x + resolution * 0.5 + offset.dx)
85
+ 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
113
86
 
114
87
  //draw the text
115
- cg.ctx.fillText(text, tx, ty)
88
+ geoCanvas.ctx.fillText(text, tx, ty)
116
89
  }
117
90
 
118
91
  //update legends
119
- this.updateLegends({ style: this, r: r, zf: zf, sColor: statColor })
92
+ this.updateLegends({ style: this, resolution: resolution, z: z, viewScale: viewScale })
120
93
  }
121
94
 
122
95
  /**
123
96
  * Build a function [0,1]->string for characters legend
124
97
  *
125
98
  * @param {Array.<string>} chars
99
+ * @param {(function(number):number)|undefined} scale
126
100
  * @returns {function(number):string}
127
101
  */
128
- static getCharLegendFun(chars) {
102
+ static textScale(chars, scale = undefined) {
129
103
  const nb = chars.length
130
- return (t) => (t == 0 ? '' : t == 1 ? chars[nb - 1] : chars[Math.floor(t * nb)])
104
+ return (t) => {
105
+ if (scale) t = scale(t)
106
+ if (t == 0) return ""
107
+ if (t >= 1) return chars[nb - 1]
108
+ return chars[Math.floor(t * nb)]
109
+ }
131
110
  }
132
111
  }