gridviz 2.0.28 → 2.0.29

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/dist/gridviz.js +34315 -0
  2. package/package.json +3 -2
  3. package/src/App.js +651 -0
  4. package/src/BackgroundLayer.js +182 -0
  5. package/src/BackgroundLayerWMS.js +131 -0
  6. package/src/Dataset.js +76 -0
  7. package/src/DatasetComponent.js +103 -0
  8. package/src/GeoCanvas.js +419 -0
  9. package/src/LabelLayer.js +190 -0
  10. package/src/Layer.js +92 -0
  11. package/src/Legend.js +86 -0
  12. package/src/LineLayer.js +150 -0
  13. package/src/Style.js +114 -0
  14. package/src/Tooltip.js +150 -0
  15. package/src/dataset/COGeo.js +24 -0
  16. package/src/dataset/CSVGrid.js +105 -0
  17. package/src/dataset/GeoTIFF.js +127 -0
  18. package/src/dataset/GridTile.js +41 -0
  19. package/src/dataset/ParquetGrid.js +123 -0
  20. package/src/dataset/TiledGrid.js +294 -0
  21. package/src/index.js +176 -0
  22. package/src/legend/ColorCategoryLegend.js +124 -0
  23. package/src/legend/ColorDiscreteLegend.js +105 -0
  24. package/src/legend/ColorLegend.js +125 -0
  25. package/src/legend/SegmentOrientationLegend.js +87 -0
  26. package/src/legend/SegmentWidthLegend.js +97 -0
  27. package/src/legend/SizeLegend.js +141 -0
  28. package/src/style/CompositionStyle.js +294 -0
  29. package/src/style/ContourStyle.js +54 -0
  30. package/src/style/DotDensityStyle.js +156 -0
  31. package/src/style/JoyPlotStyle.js +135 -0
  32. package/src/style/KernelSmoothingOldStyle.js +221 -0
  33. package/src/style/KernelSmoothingStyle.js +115 -0
  34. package/src/style/KernelSmoothingWGLStyle.js +130 -0
  35. package/src/style/LegoStyle.js +112 -0
  36. package/src/style/MosaicStyle.js +107 -0
  37. package/src/style/NinjaStarStyle.js +124 -0
  38. package/src/style/PillarStyle.js +230 -0
  39. package/src/style/SegmentStyle.js +142 -0
  40. package/src/style/ShapeColorSizeStyle.js +126 -0
  41. package/src/style/SideCatStyle.js +172 -0
  42. package/src/style/SideStyle.js +218 -0
  43. package/src/style/SquareColorCatWGLStyle.js +120 -0
  44. package/src/style/SquareColorWGLStyle.js +146 -0
  45. package/src/style/StrokeStyle.js +131 -0
  46. package/src/style/TanakaStyle.js +160 -0
  47. package/src/style/TextStyle.js +132 -0
  48. package/src/utils/Utils.js +35 -0
  49. package/src/utils/WebGLRectangleColoring.js +106 -0
  50. package/src/utils/WebGLSquareColoring.js +118 -0
  51. package/src/utils/WebGLSquareColoringAdvanced.js +230 -0
  52. package/src/utils/WebGLSquareColoringCatAdvanced.js +133 -0
  53. package/src/utils/WebGLSquareColoringKS.js +153 -0
  54. package/src/utils/stretching.js +131 -0
  55. package/src/utils/webGLUtils.js +71 -0
  56. package/src/utils/zoomUtils.js +142 -0
@@ -0,0 +1,153 @@
1
+ //@ts-check
2
+ 'use strict'
3
+
4
+ import { initShaderProgram, createShader } from './webGLUtils'
5
+ import { color } from 'd3-color'
6
+
7
+ /**
8
+ * Everything to easily draw colored squares with webGL.
9
+ * All the same size, but different fill color.
10
+ * The color interpolation is computed in the fragment shader program, by the GPU, thus it is less flexible but faster.
11
+ */
12
+ export class WebGLSquareColoringKS {
13
+ //see:
14
+ //https://webglfundamentals.org/
15
+ //https://webglfundamentals.org/webgl/lessons/fr/webgl-shaders-and-glsl.html#les-uniforms-dans-les-shaders-de-vertex
16
+ //https://thebookofshaders.com/glossary/?search=mix
17
+ //https://thebookofshaders.com/06/
18
+ //https://thebookofshaders.com/glossary/
19
+
20
+ /**
21
+ *
22
+ * @param {*} gl
23
+ * @param {Array.<String>} colors
24
+ * @param {{fun:string,alpha:number}} stretching
25
+ * @param {number} sizePix
26
+ */
27
+ constructor(gl, colors, stretching, sizePix = 10) {
28
+ /** @type {WebGLRenderingContext} */
29
+ this.gl = gl
30
+
31
+ /** @type {WebGLShader} */
32
+ const vShader = createShader(
33
+ gl,
34
+ gl.VERTEX_SHADER,
35
+ `
36
+ attribute vec2 pos;
37
+ uniform float sizePix;
38
+ uniform vec3 data[2];
39
+ uniform mat3 mat;
40
+
41
+ varying float vt;
42
+
43
+ void main() {
44
+
45
+ //one vertex for each screen pixels
46
+ //list of data as uniform
47
+ //compute smoothed t here from uniforms
48
+
49
+ //compute cell screen position
50
+ gl_Position = vec4(mat * vec3(pos, 1.0), 1.0);
51
+
52
+ //set pixel size - same for all
53
+ gl_PointSize = sizePix;
54
+
55
+ //pass the pixel value to the fragment as varying
56
+ vt = data[2];
57
+ }
58
+ `
59
+ )
60
+
61
+ //prepare fragment shader code
62
+ //declare the uniform and other variables
63
+ let fshString = `
64
+ precision mediump float;
65
+ varying float vt;
66
+ uniform float alpha;
67
+ uniform vec4 c0;
68
+ uniform vec4 c1;
69
+ uniform vec4 c2;
70
+ uniform vec4 c3;
71
+ uniform vec4 c4;
72
+ void main(void) {
73
+ if(vt == 0.0) return;
74
+ float t = 1.0 - (1.0 / alpha) * log(exp(alpha) * (1.0 - vt) + vt);
75
+ vec4 cI;
76
+ vec4 cF;
77
+ if(t<1.0/4.0) { cI=c0; cF=c1; t=t*4.0; }
78
+ else if(t<2.0/4.0) { cI=c1; cF=c2; t=4.0*t-1.0; }
79
+ else if(t<3.0/4.0) { cI=c2; cF=c3; t=4.0*t-2.0; }
80
+ else { cI=c3; cF=c4; t=4.0*t-3.0; }
81
+ gl_FragColor = mix(cI, cF, t);
82
+ }`
83
+
84
+ /** @type {WebGLShader} */
85
+ const fShader = createShader(gl, gl.FRAGMENT_SHADER, fshString)
86
+
87
+ /** @type {WebGLProgram} */
88
+ this.program = initShaderProgram(gl, vShader, fShader)
89
+ gl.useProgram(this.program)
90
+
91
+ //set uniforms
92
+
93
+ //sizePix
94
+ gl.uniform1f(gl.getUniformLocation(this.program, 'sizePix'), 1.0 * sizePix)
95
+
96
+ //stretching alpha factor
97
+ gl.uniform1f(gl.getUniformLocation(this.program, 'alpha'), stretching ? 1.0 * stretching.alpha : 0.0)
98
+
99
+ //colors
100
+ for (let i = 0; i < colors.length; i++) {
101
+ const c = color(colors[i])
102
+ gl.uniform4fv(gl.getUniformLocation(this.program, 'c' + i), [
103
+ +c.r / 255.0,
104
+ +c.g / 255.0,
105
+ +c.b / 255.0,
106
+ +c.opacity,
107
+ ])
108
+ }
109
+ }
110
+
111
+ /** */
112
+ draw(verticesBuffer, data, transfoMat) {
113
+ const gl = this.gl
114
+ const program = this.program
115
+
116
+ //vertice data
117
+ gl.bindBuffer(gl.ARRAY_BUFFER, gl.createBuffer())
118
+ gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(verticesBuffer), gl.STATIC_DRAW)
119
+ const position = gl.getAttribLocation(program, 'pos')
120
+ gl.vertexAttribPointer(
121
+ position,
122
+ 2, //numComponents
123
+ gl.FLOAT, //type
124
+ false, //normalise
125
+ 0, //stride
126
+ 0 //offset
127
+ )
128
+ gl.enableVertexAttribArray(position)
129
+
130
+ //t data
131
+ /*gl.bindBuffer(gl.ARRAY_BUFFER, gl.createBuffer());
132
+ gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(tBuffer), gl.STATIC_DRAW);
133
+ const t = gl.getAttribLocation(program, "t");
134
+ gl.vertexAttribPointer(t, 1, gl.FLOAT, false, 0, 0);
135
+ gl.enableVertexAttribArray(t);*/
136
+
137
+ //data
138
+ //gl.uniformMatrix3fv(gl.getUniformLocation(program, "mat"), false, new Float32Array(transfoMat));
139
+ gl.uniform3fv(gl.getUniformLocation(program, 'data'), 2, new Float32Array(data))
140
+
141
+ //transformation
142
+ gl.uniformMatrix3fv(gl.getUniformLocation(program, 'mat'), false, new Float32Array(transfoMat))
143
+
144
+ // Enable the depth test
145
+ //gl.enable(gl.DEPTH_TEST);
146
+ // Clear the color buffer bit
147
+ gl.clear(gl.COLOR_BUFFER_BIT)
148
+ // Set the view port
149
+ //gl.viewport(0, 0, cg.w, cg.h);
150
+
151
+ gl.drawArrays(gl.POINTS, 0, verticesBuffer.length / 2)
152
+ }
153
+ }
@@ -0,0 +1,131 @@
1
+ //@ts-check
2
+ 'use strict'
3
+
4
+ /**
5
+ * Some function [0,1]->[0,1] to stretch range of values.
6
+ * @see https://github.com/eurostat/gridviz/blob/master/docs/reference.md#stretching
7
+ * @see https://observablehq.com/@jgaffuri/stretching
8
+ */
9
+
10
+ /**
11
+ * Function [0,1]->[0,1] to stretch range of values.
12
+ * Polynomial
13
+ *
14
+ * @param {number} t The value to stretch, within [0,1]
15
+ * @param {number} alpha 1: no stretching. <1: show low values. >1: show high values.
16
+ * @returns {number} The stretched value, within [0,1]
17
+ */
18
+ export const sPow = (t, alpha = 3) => Math.pow(t, alpha)
19
+
20
+ /**
21
+ * Function [0,1]->[0,1] to stretch range of values.
22
+ * Polynomial (reverse)
23
+ *
24
+ * @param {number} t The value to stretch, within [0,1]
25
+ * @param {number} alpha 1: no stretching. <1: show low values. >1: show high values.
26
+ * @returns {number} The stretched value, within [0,1]
27
+ */
28
+ export const sPowRev = (t, alpha = 3) => 1 - Math.pow(1 - t, 1 / alpha)
29
+
30
+ /**
31
+ * Function [0,1]->[0,1] to stretch range of values.
32
+ * Exponential
33
+ *
34
+ * @param {number} t The value to stretch, within [0,1]
35
+ * @param {number} alpha 0: no stretching. -Inf: show low values. Inf: show high values.
36
+ * @returns {number} The stretched value, within [0,1]
37
+ */
38
+ export const sExp = (t, alpha = 3) => (alpha == 0 ? t : (Math.exp(t * alpha) - 1) / (Math.exp(alpha) - 1))
39
+
40
+ /**
41
+ * Function [0,1]->[0,1] to stretch range of values.
42
+ * Exponential (reverse)
43
+ *
44
+ * @param {number} t The value to stretch, within [0,1]
45
+ * @param {number} alpha 0: no stretching. -Inf: show low values. Inf: show high values.
46
+ * @returns {number} The stretched value, within [0,1]
47
+ */
48
+ export const sExpRev = (t, alpha = 3) =>
49
+ alpha == 0 ? t : 1 - (1 / alpha) * Math.log(Math.exp(alpha) * (1 - t) + t)
50
+
51
+ /**
52
+ * Function [0,1]->[0,1] to stretch range of values.
53
+ * Circle, show low values
54
+ * NB: sCircleHigh and sCircleLow are inverse functions of each other.
55
+ *
56
+ * @param {number} t The value to stretch, within [0,1]
57
+ * @param {number} alpha 0: no stretching. 1: perfect circle section
58
+ * @returns {number} The stretched value, within [0,1]
59
+ */
60
+ export const sCircleLow = (t, alpha = 0.8) => {
61
+ if (alpha == 0) return t
62
+ if (alpha == 1) return Math.sqrt(t * (2 - t))
63
+ const a = alpha / (1 - alpha)
64
+ return Math.sqrt(1 / (a * a) + t * (2 / a + 2 - t)) - 1 / a
65
+ }
66
+
67
+ /**
68
+ * Function [0,1]->[0,1] to stretch range of values.
69
+ * Circle, show high values
70
+ * NB: sCircleHigh and sCircleLow are inverse functions of each other.
71
+ *
72
+ * @param {number} t The value to stretch, within [0,1]
73
+ * @param {number} alpha 0: no stretching. 1: perfect circle section
74
+ * @returns {number} The stretched value, within [0,1]
75
+ */
76
+ export const sCircleHigh = (t, alpha = 0.8) => 1 - sCircleLow(1 - t, alpha)
77
+
78
+ /**
79
+ * Inverse functions
80
+ */
81
+
82
+ /**
83
+ * Inverse function of sExp
84
+ * @param {number} y
85
+ * @param {number} alpha
86
+ * @returns {number}
87
+ */
88
+ export const sExpInverse = (y, alpha = 3) =>
89
+ alpha == 0 ? y : (1 / alpha) * Math.log(1 - y + y * Math.exp(alpha))
90
+
91
+ /**
92
+ * Inverse function of sExpRev
93
+ * @param {number} y
94
+ * @param {number} alpha
95
+ * @returns {number}
96
+ */
97
+ export const sExpRevInverse = (y, alpha = 3) => (Math.exp(-alpha * y) - 1) / (Math.exp(-alpha) - 1)
98
+
99
+ /**
100
+ * Inverse function of sPow
101
+ * @param {number} y
102
+ * @param {number} alpha
103
+ * @returns {number}
104
+ */
105
+
106
+ export const sPowInverse = (y, alpha = 3) => Math.pow(y, 1 / alpha)
107
+
108
+ /**
109
+ * Inverse function of sPowRev
110
+ * @param {number} y
111
+ * @param {number} alpha
112
+ * @returns {number}
113
+ */
114
+ export const sPowRevInverse = (y, alpha = 3) => 1 - Math.pow(1 - y, alpha)
115
+
116
+ //test code
117
+ /*
118
+ for (let i = 0; i <= 1; i += 0.001) {
119
+ //const v = gviz.sExp(gviz.sExpInverse(i));
120
+ //const v = gviz.sExpInverse(gviz.sExp(i));
121
+ //const v = gviz.sExpRev(gviz.sExpRevInverse(i));
122
+ //const v = gviz.sExpRevInverse(gviz.sExpRev(i));
123
+ //const v = gviz.sPow(gviz.sPowInverse(i));
124
+ //const v = gviz.sPowInverse(gviz.sPow(i));
125
+ //const v = gviz.sPowRev(gviz.sPowRevInverse(i));
126
+ //const v = gviz.sPowRevInverse(gviz.sPowRev(i));
127
+ //const v = gviz.sCircleLow(gviz.sCircleHigh(i));
128
+ //const v = gviz.sCircleHigh(gviz.sCircleLow(i));
129
+ console.log(i - v)
130
+ }
131
+ */
@@ -0,0 +1,71 @@
1
+ //@ts-check
2
+ 'use strict'
3
+
4
+ /**
5
+ * @param {string} width
6
+ * @param {string} height
7
+ * @param {object} opts
8
+ * @returns {{canvas:HTMLCanvasElement, gl:WebGLRenderingContext}}
9
+ */
10
+ export function makeWebGLCanvas(width, height, opts) {
11
+ const canvas = document.createElement('canvas')
12
+ canvas.setAttribute('width', width)
13
+ canvas.setAttribute('height', height)
14
+ const gl = canvas.getContext('webgl', opts)
15
+ if (!gl) {
16
+ throw new Error('Unable to initialize WebGL. Your browser or machine may not support it.')
17
+ }
18
+ return { canvas: canvas, gl: gl }
19
+ }
20
+
21
+ /**
22
+ * Initialize a shader program, so WebGL knows how to draw our data
23
+ *
24
+ * @param {WebGLRenderingContext} gl
25
+ * @param {...WebGLShader} shaders
26
+ * @returns {WebGLProgram}
27
+ */
28
+ export function initShaderProgram(gl, ...shaders) {
29
+ /** @type {WebGLProgram|null} */
30
+ const program = gl.createProgram()
31
+ if (program == null) throw new Error('Cannot create webGL program')
32
+ for (const shader of shaders) gl.attachShader(program, shader)
33
+ gl.linkProgram(program)
34
+ if (gl.getProgramParameter(program, gl.LINK_STATUS)) return program
35
+ throw new Error(gl.getProgramInfoLog(program) || 'Cannot create webGL program (2)')
36
+ }
37
+
38
+ /**
39
+ * Creates a shader of the given type, uploads the source and compiles it.
40
+ *
41
+ * @param {WebGLRenderingContext} gl
42
+ * @param {number} type
43
+ * @param {...string} sources
44
+ * @returns {WebGLShader}
45
+ */
46
+ export function createShader(gl, type, ...sources) {
47
+ /** @type {WebGLShader|null} */
48
+ const shader = gl.createShader(type)
49
+ if (shader == null) throw new Error('Cannot create webGL shader')
50
+ gl.shaderSource(shader, sources.join('\n'))
51
+ gl.compileShader(shader)
52
+ if (gl.getShaderParameter(shader, gl.COMPILE_STATUS)) return shader
53
+ throw new Error(gl.getShaderInfoLog(shader) || 'Cannot create webGL shader (2)')
54
+ }
55
+
56
+ /**
57
+ * Check if webGL is supported
58
+ *
59
+ * @returns {boolean}
60
+ */
61
+ export function checkWebGLSupport() {
62
+ try {
63
+ const canvas = document.createElement('canvas')
64
+ return !!window.WebGLRenderingContext &&
65
+ (canvas.getContext('webgl') || canvas.getContext('experimental-webgl'))
66
+ ? true
67
+ : false
68
+ } catch (err) {
69
+ return false
70
+ }
71
+ }
@@ -0,0 +1,142 @@
1
+ //@ts-check
2
+ 'use strict'
3
+
4
+ /**
5
+ *
6
+ * @param {import("../App").App} app
7
+ * @param {number} zfTarget
8
+ * @param {number} factor
9
+ * @param {number} delayMs
10
+ * @param {function|undefined} callback
11
+ * @param {number} delayBeforeCallBackMs
12
+ * @returns
13
+ */
14
+ export function zoomTo(
15
+ app,
16
+ zfTarget,
17
+ factor = 1.01,
18
+ delayMs = 0,
19
+ callback = undefined,
20
+ delayBeforeCallBackMs = 0
21
+ ) {
22
+ //ensure good factor value: >1
23
+ factor = factor || 1.01
24
+ if (factor < 1) {
25
+ console.error('Unexpected value for factor: ' + factor + '. Set to default value 1.01')
26
+ factor = 1.01
27
+ }
28
+
29
+ const zfIni = app.getZoomFactor()
30
+ if (zfTarget == zfIni) return
31
+ if (zfTarget < zfIni) factor = 1 / factor
32
+ let zf = zfIni
33
+ let timer = setInterval(() => {
34
+ //compute new zoom level
35
+ zf = app.getZoomFactor() * factor
36
+ if (zfTarget > zfIni && zf > zfTarget) zf = zfTarget
37
+ if (zfTarget < zfIni && zf < zfTarget) zf = zfTarget
38
+
39
+ //set new zoom level
40
+ app.setZoomFactor(zf)
41
+ app.redraw()
42
+
43
+ //target reached
44
+ if (zf == zfTarget) {
45
+ clearInterval(timer)
46
+ //trigger callback, if any
47
+ if (callback)
48
+ setTimeout(() => {
49
+ callback()
50
+ }, delayBeforeCallBackMs)
51
+ }
52
+ }, delayMs)
53
+ return timer
54
+ }
55
+
56
+ /**
57
+ *
58
+ * @param {import("../App").App} app
59
+ * @param {number} xTarget
60
+ * @param {number} yTarget
61
+ * @param {number} zfTarget
62
+ * @param {number} progressFactorPix
63
+ * @param {number} delayMs
64
+ * @param {function|undefined} callback
65
+ * @param {number} delayBeforeCallBackMs
66
+ * @returns
67
+ */
68
+ export function goToStraight(
69
+ app,
70
+ xTarget = NaN,
71
+ yTarget = NaN,
72
+ zfTarget = NaN,
73
+ progressFactorPix = 5,
74
+ delayMs = 0,
75
+ callback = undefined,
76
+ delayBeforeCallBackMs = 0
77
+ ) {
78
+ //store initial position/zoom
79
+ const zfIni = app.getZoomFactor()
80
+ const cIni = app.getGeoCenter()
81
+
82
+ //default
83
+ xTarget = isNaN(xTarget) ? cIni.x : xTarget
84
+ yTarget = isNaN(yTarget) ? cIni.y : yTarget
85
+ zfTarget = isNaN(zfTarget) ? zfIni : zfTarget
86
+
87
+ //prepare for pan
88
+ const dx = xTarget - cIni.x
89
+ const dy = yTarget - cIni.y
90
+ let d = Math.hypot(dx, dy)
91
+ const ddx = (progressFactorPix * zfIni * dx) / d
92
+ const ddy = (progressFactorPix * zfIni * dy) / d
93
+
94
+ //prepare for zoom
95
+ let r = zfTarget / zfIni
96
+ const n = d / (progressFactorPix * zfIni)
97
+ const zoomFactor = d > 0 ? Math.pow(r, 1 / n) : Math.pow(r, 1 / 10) //TODO not 10 ?
98
+
99
+ //timer
100
+ let timer = setInterval(() => {
101
+ //compute and set new position
102
+ if (d > 0) {
103
+ const c = app.getGeoCenter()
104
+ let nx = c.x + ddx
105
+ let ny = c.y + ddy
106
+ //if went too far, stop at target values
107
+ if (nx < xTarget && xTarget < c.x) nx = xTarget
108
+ if (c.x < xTarget && xTarget < nx) nx = xTarget
109
+ if (ny < yTarget && yTarget < c.y) ny = yTarget
110
+ if (c.y < yTarget && yTarget < ny) ny = yTarget
111
+ app.setGeoCenter({ x: nx, y: ny })
112
+ if (nx == xTarget && ny == yTarget) d = 0
113
+ }
114
+
115
+ //compute and set new zoom
116
+ if (r != 1) {
117
+ const zf = app.getZoomFactor()
118
+ let nzf = zoomFactor * zf
119
+ //if went too far, stop at target values
120
+ if (nzf < zfTarget && zfTarget < zf) nzf = zfTarget
121
+ if (zf < zfTarget && zfTarget < nzf) nzf = zfTarget
122
+ app.setZoomFactor(nzf)
123
+ if (nzf == zfTarget) r = 1
124
+ }
125
+
126
+ //redraw
127
+ app.redraw()
128
+
129
+ //if target reached, stop
130
+ if (d == 0 && r == 1) {
131
+ //console.log("OK !")
132
+ clearInterval(timer)
133
+ //trigger callback, if any
134
+ if (callback)
135
+ setTimeout(() => {
136
+ callback()
137
+ }, delayBeforeCallBackMs)
138
+ }
139
+ }, delayMs)
140
+
141
+ return timer
142
+ }