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.
- package/README.md +2 -3
- package/dist/gridviz.js +4971 -5654
- package/dist/gridviz.min.js +1 -1
- package/package.json +3 -2
- package/src/button/Button.js +14 -15
- package/src/button/FullscreenButton.js +14 -14
- package/src/button/ZoomButtons.js +2 -2
- package/src/{DatasetComponent.js → core/Dataset.js} +33 -12
- package/src/core/Drawable.js +60 -0
- package/src/{GeoCanvas.js → core/GeoCanvas.js} +65 -155
- package/src/core/Layer.js +24 -0
- package/src/{Legend.js → core/Legend.js} +24 -1
- package/src/core/Map.js +460 -0
- package/src/core/MultiResolutionDataset.js +96 -0
- package/src/core/Style.js +84 -0
- package/src/dataset/COGeo.js +5 -5
- package/src/dataset/CSVGrid.js +43 -41
- package/src/dataset/GeoTIFF.js +9 -14
- package/src/dataset/{LGrid.js → JSGrid.js} +9 -10
- package/src/dataset/TiledGrid.js +68 -45
- package/src/index.js +22 -29
- package/src/{BackgroundLayer.js → layer/BackgroundLayer.js} +27 -52
- package/src/{BackgroundLayerWMS.js → layer/BackgroundLayerWMS.js} +21 -45
- package/src/{LineLayer.js → layer/GeoJSONLayer.js} +22 -23
- package/src/layer/GridLayer.js +140 -0
- package/src/{LabelLayer.js → layer/LabelLayer.js} +25 -23
- package/src/legend/ColorCategoryLegend.js +9 -24
- package/src/legend/ColorDiscreteLegend.js +20 -24
- package/src/legend/ColorLegend.js +21 -33
- package/src/legend/OrientationLegend.js +90 -0
- package/src/legend/SizeLegend.js +153 -72
- package/src/style/CompositionStyle.js +71 -84
- package/src/style/ContourStyle.js +2 -36
- package/src/style/DotDensityStyle.js +50 -73
- package/src/style/IsoFenceStyle.js +59 -70
- package/src/style/JoyPlotStyle.js +41 -48
- package/src/style/LegoStyle.js +4 -4
- package/src/style/MosaicStyle.js +30 -39
- package/src/style/NinjaStarStyle.js +45 -68
- package/src/style/PillarStyle.js +70 -93
- package/src/style/SegmentStyle.js +37 -75
- package/src/style/ShapeColorSizeStyle.js +49 -98
- package/src/style/SideCategoryStyle.js +112 -0
- package/src/style/SideStyle.js +99 -133
- package/src/style/{SquareColorCatWGLStyle.js → SquareColorCategoryWebGLStyle.js} +28 -41
- package/src/style/{SquareColorWGLStyle.js → SquareColorWebGLStyle.js} +28 -49
- package/src/style/StrokeStyle.js +43 -70
- package/src/style/TanakaStyle.js +34 -132
- package/src/style/TextStyle.js +43 -64
- package/src/style/TimeSeriesStyle.js +44 -75
- package/src/utils/WebGLSquareColoringAdvanced.js +4 -4
- package/src/utils/scale.js +202 -0
- package/src/utils/stretching.js +86 -129
- package/src/utils/{Utils.js → utils.js} +15 -0
- package/src/utils/webGLUtils.js +4 -5
- package/src/utils/zoomUtils.js +30 -30
- package/src/App.js +0 -760
- package/src/Dataset.js +0 -76
- package/src/Layer.js +0 -103
- package/src/Style.js +0 -131
- package/src/dataset/GridTile.js +0 -41
- package/src/legend/SegmentOrientationLegend.js +0 -87
- package/src/legend/SegmentWidthLegend.js +0 -101
- package/src/style/CompositionStyle_.js +0 -293
- package/src/style/ShapeColorSizeStyle_.js +0 -108
- package/src/style/SideCatStyle.js +0 -172
- /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
|
/** @typedef {"first"|"bottom"|"center"|"top"|"last"} AnchorModeYEnum */
|
|
7
7
|
|
|
@@ -27,65 +27,46 @@ export class TimeSeriesStyle extends Style {
|
|
|
27
27
|
|
|
28
28
|
//x
|
|
29
29
|
/** in geo unit
|
|
30
|
-
* @type {function(import("../Dataset.js").Cell,number,number):number} */
|
|
31
|
-
this.offsetX = opts.offsetX || ((c, r,
|
|
32
|
-
/** @type {function(import("../Dataset.js").Cell,number,number):number} */
|
|
33
|
-
this.width = opts.width || ((c, r,
|
|
30
|
+
* @type {function(import("../core/Dataset.js").Cell,number,number):number} */
|
|
31
|
+
this.offsetX = opts.offsetX || ((c, r, z) => 0)
|
|
32
|
+
/** @type {function(import("../core/Dataset.js").Cell,number,number):number} */
|
|
33
|
+
this.width = opts.width || ((c, r, z) => r)
|
|
34
34
|
|
|
35
35
|
//y
|
|
36
36
|
/** in geo unit
|
|
37
|
-
* @type {function(import("../Dataset.js").Cell,number,number):number} */
|
|
38
|
-
this.offsetY = opts.offsetY || ((c, r,
|
|
39
|
-
/** @type {function(import("../Dataset.js").Cell,number,number):number} */
|
|
40
|
-
this.height = opts.height || ((c, r,
|
|
41
|
-
/** @type {function(import("../Dataset.js").Cell,number,number):AnchorModeYEnum} */
|
|
42
|
-
this.anchorModeY = opts.anchorModeY || ((c, r,
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* @type {string} */
|
|
47
|
-
this.lineWidthCol = opts.lineWidthCol
|
|
37
|
+
* @type {function(import("../core/Dataset.js").Cell,number,number):number} */
|
|
38
|
+
this.offsetY = opts.offsetY || ((c, r, z) => 0)
|
|
39
|
+
/** @type {function(import("../core/Dataset.js").Cell,number,number):number} */
|
|
40
|
+
this.height = opts.height || ((c, r, z) => r)
|
|
41
|
+
/** @type {function(import("../core/Dataset.js").Cell,number,number):AnchorModeYEnum} */
|
|
42
|
+
this.anchorModeY = opts.anchorModeY || ((c, r, z) => "center")
|
|
48
43
|
|
|
49
44
|
/** A function returning the width of the line, in geo unit
|
|
50
|
-
* @type {function(
|
|
51
|
-
this.lineWidth = opts.lineWidth || ((v, r, s,
|
|
52
|
-
|
|
53
|
-
/**
|
|
54
|
-
* @type {string} */
|
|
55
|
-
this.colorCol = opts.colorCol
|
|
56
|
-
|
|
57
|
-
/** A function returning the color of the cell.
|
|
58
|
-
* @type {function(number,number,import("../Style.js").Stat|undefined,number):string} */
|
|
59
|
-
this.color = opts.color || ((v, r, s, zf) => 'black')
|
|
45
|
+
* @type {function(import('../core/Dataset.js').Cell, number, number, object):number} */
|
|
46
|
+
this.lineWidth = opts.lineWidth || ((v, r, s, z) => 1.5 * z)
|
|
60
47
|
|
|
48
|
+
/** A function returning the color of the chart.
|
|
49
|
+
* @type {function(import('../core/Dataset.js').Cell, number, number, object):string} */
|
|
50
|
+
this.color = opts.color || (() => "black") //(c,r,z,vs) => {}
|
|
61
51
|
}
|
|
62
52
|
|
|
63
53
|
/**
|
|
64
54
|
* Draw cells as text.
|
|
65
55
|
*
|
|
66
|
-
* @param {Array.<import("../Dataset.js").Cell>} cells
|
|
67
|
-
* @param {
|
|
68
|
-
* @param {
|
|
56
|
+
* @param {Array.<import("../core/Dataset.js").Cell>} cells
|
|
57
|
+
* @param {import("../core/GeoCanvas.js").GeoCanvas} geoCanvas
|
|
58
|
+
* @param {number} resolution
|
|
69
59
|
*/
|
|
70
|
-
draw(cells,
|
|
60
|
+
draw(cells, geoCanvas, resolution) {
|
|
71
61
|
|
|
72
62
|
//filter
|
|
73
63
|
if (this.filter) cells = cells.filter(this.filter)
|
|
74
64
|
|
|
75
|
-
//
|
|
76
|
-
const
|
|
65
|
+
//
|
|
66
|
+
const z = geoCanvas.view.z
|
|
77
67
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
//and compute size variable statistics
|
|
81
|
-
statWidth = Style.getStatistics(cells, (c) => c[this.lineWidthCol], true)
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
let statColor
|
|
85
|
-
if (this.colorCol) {
|
|
86
|
-
//compute color variable statistics
|
|
87
|
-
statColor = Style.getStatistics(cells, (c) => c[this.colorCol], true)
|
|
88
|
-
}
|
|
68
|
+
//get view scale
|
|
69
|
+
const viewScale = this.viewScale ? this.viewScale(cells, resolution, z) : undefined
|
|
89
70
|
|
|
90
71
|
//compute cell amplitude
|
|
91
72
|
const getAmplitude = c => {
|
|
@@ -111,40 +92,36 @@ export class TimeSeriesStyle extends Style {
|
|
|
111
92
|
|
|
112
93
|
const nb = this.ts.length
|
|
113
94
|
|
|
114
|
-
|
|
115
|
-
//in geo coordinates
|
|
116
|
-
cg.setCanvasTransform()
|
|
117
|
-
|
|
118
|
-
cg.ctx.lineCap = "butt"
|
|
95
|
+
geoCanvas.ctx.lineCap = "butt"
|
|
119
96
|
for (let c of cells) {
|
|
120
97
|
|
|
121
98
|
//line width
|
|
122
99
|
/** @type {number|undefined} */
|
|
123
|
-
const wG = this.lineWidth ? this.lineWidth(c
|
|
100
|
+
const wG = this.lineWidth ? this.lineWidth(c, resolution, z, viewScale) : undefined
|
|
124
101
|
if (!wG || wG < 0) continue
|
|
125
102
|
|
|
126
103
|
//line color
|
|
127
104
|
/** @type {string|undefined} */
|
|
128
|
-
const col = this.color ? this.color(c
|
|
105
|
+
const col = this.color ? this.color(c, resolution, z, viewScale) : undefined
|
|
129
106
|
if (!col) continue
|
|
130
107
|
|
|
131
108
|
|
|
132
109
|
//x
|
|
133
|
-
const offX = this.offsetX ? this.offsetX(c,
|
|
110
|
+
const offX = this.offsetX ? this.offsetX(c, resolution, z) : 0
|
|
134
111
|
if (offX == undefined || isNaN(offX)) continue
|
|
135
|
-
const w = this.width ? this.width(c,
|
|
112
|
+
const w = this.width ? this.width(c, resolution, z) : resolution
|
|
136
113
|
if (w == undefined || isNaN(w)) continue
|
|
137
114
|
|
|
138
115
|
//y
|
|
139
|
-
const offY = this.offsetY ? this.offsetY(c,
|
|
116
|
+
const offY = this.offsetY ? this.offsetY(c, resolution, z) : 0
|
|
140
117
|
if (offY == undefined || isNaN(offY)) continue
|
|
141
|
-
const h = this.height ? this.height(c,
|
|
118
|
+
const h = this.height ? this.height(c, resolution, z) : resolution
|
|
142
119
|
if (h == undefined || isNaN(h)) continue
|
|
143
|
-
const anchY = this.anchorModeY ? this.anchorModeY(c,
|
|
120
|
+
const anchY = this.anchorModeY ? this.anchorModeY(c, resolution, z) : "center"
|
|
144
121
|
if (!anchY) continue
|
|
145
122
|
|
|
146
|
-
|
|
147
|
-
|
|
123
|
+
geoCanvas.ctx.lineWidth = wG
|
|
124
|
+
geoCanvas.ctx.strokeStyle = col
|
|
148
125
|
|
|
149
126
|
//compute anchor Y figures
|
|
150
127
|
let val0, y0
|
|
@@ -171,7 +148,7 @@ export class TimeSeriesStyle extends Style {
|
|
|
171
148
|
if (val == undefined) continue
|
|
172
149
|
if (val0 == undefined || val > val0) val0 = val
|
|
173
150
|
}
|
|
174
|
-
y0 =
|
|
151
|
+
y0 = resolution
|
|
175
152
|
} else if (anchY === "center") {
|
|
176
153
|
//get min and max
|
|
177
154
|
let min, max
|
|
@@ -182,7 +159,7 @@ export class TimeSeriesStyle extends Style {
|
|
|
182
159
|
if (max == undefined || val > max) max = val
|
|
183
160
|
}
|
|
184
161
|
val0 = (+max + +min) * 0.5
|
|
185
|
-
y0 =
|
|
162
|
+
y0 = resolution / 2
|
|
186
163
|
} else {
|
|
187
164
|
console.log("Unexpected anchorModeY: " + anchY)
|
|
188
165
|
continue;
|
|
@@ -209,8 +186,8 @@ export class TimeSeriesStyle extends Style {
|
|
|
209
186
|
//handle first point
|
|
210
187
|
let v0 = c[this.ts[0]]
|
|
211
188
|
if (!this.noData(v0)) {
|
|
212
|
-
|
|
213
|
-
|
|
189
|
+
geoCanvas.ctx.beginPath()
|
|
190
|
+
geoCanvas.ctx.moveTo(c.x + offX, c.y + y0 + (v0 - val0) * h / ampMax + offY)
|
|
214
191
|
}
|
|
215
192
|
//console.log(v0, isNaN(v0))
|
|
216
193
|
|
|
@@ -225,18 +202,18 @@ export class TimeSeriesStyle extends Style {
|
|
|
225
202
|
|
|
226
203
|
//second point 'no data'
|
|
227
204
|
} else if (!this.noData(v0) && this.noData(v1)) {
|
|
228
|
-
|
|
205
|
+
geoCanvas.ctx.stroke()
|
|
229
206
|
|
|
230
207
|
//first point 'no data'
|
|
231
208
|
} else if (this.noData(v0) && !this.noData(v1)) {
|
|
232
|
-
|
|
233
|
-
|
|
209
|
+
geoCanvas.ctx.beginPath()
|
|
210
|
+
geoCanvas.ctx.moveTo(c.x + i * sX + offX, c.y + y0 + (v1 - val0) * h / ampMax + offY)
|
|
234
211
|
|
|
235
212
|
//both points have data: trace line
|
|
236
213
|
} else {
|
|
237
|
-
|
|
214
|
+
geoCanvas.ctx.lineTo(c.x + i * sX + offX, c.y + y0 + (v1 - val0) * h / ampMax + offY)
|
|
238
215
|
//if it is the last point, stroke
|
|
239
|
-
if (i == nb - 1)
|
|
216
|
+
if (i == nb - 1) geoCanvas.ctx.stroke()
|
|
240
217
|
}
|
|
241
218
|
v0 = v1
|
|
242
219
|
}
|
|
@@ -244,15 +221,7 @@ export class TimeSeriesStyle extends Style {
|
|
|
244
221
|
}
|
|
245
222
|
|
|
246
223
|
//update legend, if any
|
|
247
|
-
this.updateLegends({
|
|
248
|
-
widthFun: this.lineWidth,
|
|
249
|
-
r: r,
|
|
250
|
-
zf: zf,
|
|
251
|
-
sColor: statColor,
|
|
252
|
-
//sLength: statLength,
|
|
253
|
-
sWidth: statWidth,
|
|
254
|
-
})
|
|
255
|
-
|
|
224
|
+
this.updateLegends({ style: this, resolution: resolution, z: z, viewScale: viewScale })
|
|
256
225
|
}
|
|
257
226
|
|
|
258
227
|
}
|
|
@@ -70,7 +70,7 @@ export class WebGLSquareColoringAdvanced {
|
|
|
70
70
|
if (stretching.fun == 'pow')
|
|
71
71
|
//sPow = (t, alpha = 3) => Math.pow(t, alpha);
|
|
72
72
|
fshString += ' float t = pow(vt, alpha);\n'
|
|
73
|
-
else if (stretching.fun == '
|
|
73
|
+
else if (stretching.fun == 'powInv')
|
|
74
74
|
//sPowRev = (t, alpha = 3) => 1 - Math.pow(1 - t, 1 / alpha);
|
|
75
75
|
fshString += ' float t = 1.0-pow(1.0-vt, 1.0/alpha);\n'
|
|
76
76
|
else if (stretching.fun == 'exp')
|
|
@@ -79,13 +79,13 @@ export class WebGLSquareColoringAdvanced {
|
|
|
79
79
|
stretching.alpha == 0
|
|
80
80
|
? `float t = vt;`
|
|
81
81
|
: ' float t = (exp(vt * alpha) - 1.0) / (exp(alpha) - 1.0);\n'
|
|
82
|
-
else if (stretching.fun == '
|
|
82
|
+
else if (stretching.fun == 'log')
|
|
83
83
|
//sExpRev = (t, alpha = 3) => alpha == 0 ? t : 1 - (1 / alpha) * Math.log(Math.exp(alpha) * (1 - t) + t);
|
|
84
84
|
fshString +=
|
|
85
85
|
stretching.alpha == 0
|
|
86
86
|
? `float t = vt;`
|
|
87
87
|
: ' float t = 1.0 - (1.0 / alpha) * log(exp(alpha) * (1.0 - vt) + vt);\n'
|
|
88
|
-
else if (stretching.fun == '
|
|
88
|
+
else if (stretching.fun == 'circle') {
|
|
89
89
|
if (stretching.alpha == 0)
|
|
90
90
|
//if (alpha == 0) return t;
|
|
91
91
|
fshString += ' float t = vt;\n'
|
|
@@ -99,7 +99,7 @@ export class WebGLSquareColoringAdvanced {
|
|
|
99
99
|
' float a = alpha / (1.0 - alpha);\n' +
|
|
100
100
|
' float t = sqrt(1.0 / (a * a) + vt * ( 2.0/a + 2.0 - vt )) - 1.0 / a;\n'
|
|
101
101
|
}
|
|
102
|
-
} else if (stretching.fun == '
|
|
102
|
+
} else if (stretching.fun == 'circleInv') {
|
|
103
103
|
// 1 - sCircleLow(1 - t, alpha)
|
|
104
104
|
if (stretching.alpha == 0)
|
|
105
105
|
//if (alpha == 0) return t;
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
//@ts-check
|
|
2
|
+
'use strict'
|
|
3
|
+
|
|
4
|
+
import { extent, max } from "d3-array"
|
|
5
|
+
import { scaleQuantile } from "d3-scale"
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* A scale is simply a function that map a domain to a range.
|
|
9
|
+
* @typedef {function(number):number} Scale */
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* A scale whose range is a color (string).
|
|
13
|
+
* @typedef {function(number):string} ColorScale */
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Generic function for view scale - continuous
|
|
19
|
+
*
|
|
20
|
+
* @param {{ valueFunction:function(import("../core/Dataset").Cell):number, minValue?:number, minSizePix?:number, maxSizeFactor?:number, range?:[number, number], domain?:[number, number], stretching?:function(number):number }} opts
|
|
21
|
+
* @returns {function(Array.<import("../core/Dataset").Cell>):Scale}
|
|
22
|
+
*/
|
|
23
|
+
export const viewScale = (opts) => {
|
|
24
|
+
const valueFunction = opts.valueFunction
|
|
25
|
+
const minValue = opts.minValue || 0
|
|
26
|
+
const minSizePix = opts.minSizePix || 0
|
|
27
|
+
const maxSizeFactor = opts.maxSizeFactor || 1
|
|
28
|
+
const stretching = opts.stretching
|
|
29
|
+
const range_ = opts.range
|
|
30
|
+
const domain_ = opts.domain
|
|
31
|
+
return (cells, r, z) => {
|
|
32
|
+
const domain = domain_ || [minValue, max(cells, valueFunction)]
|
|
33
|
+
const range = range_ || [minSizePix * z, r * maxSizeFactor]
|
|
34
|
+
return t => {
|
|
35
|
+
//scale to [0,1]
|
|
36
|
+
t = (t - domain[0]) / (domain[1] - domain[0])
|
|
37
|
+
//stretch
|
|
38
|
+
if (stretching) t = stretching(t)
|
|
39
|
+
//scale to range
|
|
40
|
+
return range[0] + t * (range[1] - range[0])
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Generic function for view scale - quantile
|
|
48
|
+
*
|
|
49
|
+
* @param {{ valueFunction:function(import("../core/Dataset").Cell):number, classNumber?:number, minSizePix?:number, maxSizeFactor?:number }} opts
|
|
50
|
+
* @returns {function(Array.<import("../core/Dataset").Cell>):Scale}
|
|
51
|
+
*/
|
|
52
|
+
export const viewScaleQuantile = (opts) => {
|
|
53
|
+
const valueFunction = opts.valueFunction
|
|
54
|
+
const classNumber = opts.classNumber || 12
|
|
55
|
+
const minSizePix = opts.minSizePix || 1
|
|
56
|
+
const maxSizeFactor = opts.maxSizeFactor || 1
|
|
57
|
+
const scale = scaleQuantile()
|
|
58
|
+
return (cells, r, z) => {
|
|
59
|
+
scale.domain(cells.map(valueFunction))
|
|
60
|
+
const minSizeGeo = minSizePix * z, maxSizeGeo = r * maxSizeFactor
|
|
61
|
+
scale.range(Array.from({ length: classNumber }, (_, i) => minSizeGeo + i * (maxSizeGeo - minSizeGeo) / (classNumber - 1)))
|
|
62
|
+
scale.breaks = scale.quantiles()
|
|
63
|
+
scale.values = scale.range()
|
|
64
|
+
return scale;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Generic function for color view scale - continuous
|
|
75
|
+
*
|
|
76
|
+
* @param {{ valueFunction:function(import("../core/Dataset").Cell):number, colorScale?:function(number):string, stretching?:function(number):number }} opts
|
|
77
|
+
* @returns {function(Array.<import("../core/Dataset").Cell>):ColorScale}
|
|
78
|
+
*/
|
|
79
|
+
export const viewScaleColor = (opts) => {
|
|
80
|
+
const valueFunction = opts.valueFunction
|
|
81
|
+
const colorScale = opts.colorScale || (() => "purple")
|
|
82
|
+
const stretching = opts.stretching
|
|
83
|
+
return (cells) => {
|
|
84
|
+
if (cells.length == 0 || !cells) return
|
|
85
|
+
/** @type {[undefined, undefined] | [number, number]} */
|
|
86
|
+
const domain = extent(cells, valueFunction)
|
|
87
|
+
if (domain[0] == undefined) return
|
|
88
|
+
const amplitude = domain[1] - domain[0]
|
|
89
|
+
const scale = t => {
|
|
90
|
+
//scale to [0,1]
|
|
91
|
+
t = (t - domain[0]) / amplitude
|
|
92
|
+
//stretch
|
|
93
|
+
if (stretching) t = stretching(t)
|
|
94
|
+
return colorScale(t)
|
|
95
|
+
}
|
|
96
|
+
//function that return the domain value from the [0,1] range.
|
|
97
|
+
scale.invert = t => {
|
|
98
|
+
if (stretching) t = stretching.invert(t)
|
|
99
|
+
return domain[0] + t * amplitude
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return scale;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Generic function for color view scale - quantile
|
|
108
|
+
*
|
|
109
|
+
* @param {{ valueFunction:function(import("../core/Dataset").Cell):number, classNumber?:number, colors?:Array.<string>, colorScale?:function(number):string }} opts
|
|
110
|
+
* @returns {function(Array.<import("../core/Dataset").Cell>):ColorScale}
|
|
111
|
+
*/
|
|
112
|
+
export const viewScaleColorQuantile = (opts) => {
|
|
113
|
+
const valueFunction = opts.valueFunction
|
|
114
|
+
const classNumber = opts.classNumber || 12
|
|
115
|
+
|
|
116
|
+
let colors = opts.colors
|
|
117
|
+
if (opts.colorScale) colors = discreteColors(opts.colorScale, classNumber)
|
|
118
|
+
colors = colors || Array.from({ length: classNumber }, (_, i) => "rgb(" + Math.floor(255 * i / (classNumber - 1)) + ",150,150)")
|
|
119
|
+
|
|
120
|
+
const scale = scaleQuantile().range(colors)
|
|
121
|
+
return (cells) => {
|
|
122
|
+
scale.domain(cells.map(valueFunction));
|
|
123
|
+
scale.breaks = scale.quantiles()
|
|
124
|
+
scale.colors = colors
|
|
125
|
+
return scale;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* combine view scale functions
|
|
134
|
+
*
|
|
135
|
+
* @param {*} obj
|
|
136
|
+
* @returns
|
|
137
|
+
*/
|
|
138
|
+
export const viewScaleCombination = (obj) => {
|
|
139
|
+
//obj: prop and a function to call
|
|
140
|
+
return (cells, r, z) => {
|
|
141
|
+
const out = {}
|
|
142
|
+
for (const p in obj) { out[p] = obj[p](cells, r, z) }
|
|
143
|
+
return out
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Return a classifier function from break values.
|
|
154
|
+
* The classifier function returns the class id (from 0 to breaks.length) from a value to classifiy.
|
|
155
|
+
* @param {Array.<number>} breaks the breaks
|
|
156
|
+
*/
|
|
157
|
+
export function classifier(breaks) {
|
|
158
|
+
const bl = breaks.length
|
|
159
|
+
const classifier = value => {
|
|
160
|
+
let i = 0
|
|
161
|
+
while (i < bl) {
|
|
162
|
+
const break_ = breaks[i]
|
|
163
|
+
if (value <= break_) return i
|
|
164
|
+
i++
|
|
165
|
+
}
|
|
166
|
+
return i
|
|
167
|
+
}
|
|
168
|
+
classifier.breaks = breaks
|
|
169
|
+
return classifier
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Return a color classifier function from break values.
|
|
176
|
+
* The classifier function returns the color from a value to classifiy.
|
|
177
|
+
* There should be one color more than break values.
|
|
178
|
+
* @param {Array.<number>} breaks the breaks
|
|
179
|
+
* @param {Array.<string>} colors the colors
|
|
180
|
+
*/
|
|
181
|
+
export function colorClassifier(breaks, colors) {
|
|
182
|
+
const classifier_ = classifier(breaks)
|
|
183
|
+
const colorClassifier = value => colors[classifier_(value)]
|
|
184
|
+
colorClassifier.breaks = breaks
|
|
185
|
+
colorClassifier.colors = colors
|
|
186
|
+
return colorClassifier
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Make array of colors from a colorScale.
|
|
191
|
+
* It is a kind of sampling, or un-interpolation
|
|
192
|
+
*
|
|
193
|
+
* @param {function(number):string} colorScale
|
|
194
|
+
* @param {number} nb
|
|
195
|
+
*/
|
|
196
|
+
export function discreteColors(colorScale, nb) {
|
|
197
|
+
if (nb == 1) return [colorScale(0.5)]
|
|
198
|
+
const out = []
|
|
199
|
+
for (let i = 0; i < nb; i++)
|
|
200
|
+
out.push(colorScale(i / (nb - 1)))
|
|
201
|
+
return out
|
|
202
|
+
}
|