gridviz 3.0.1 → 3.0.3
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 +36 -33
- package/dist/gridviz.js +1300 -200
- package/dist/gridviz.min.js +1 -1
- package/package.json +4 -3
- package/src/button/Button.js +1 -0
- package/src/button/FullscreenButton.js +1 -0
- package/src/button/ZoomButtons.js +1 -0
- package/src/core/Dataset.js +1 -0
- package/src/core/Drawable.js +1 -0
- package/src/core/GeoCanvas.js +1 -0
- package/src/core/Layer.js +1 -0
- package/src/core/Legend.js +1 -0
- package/src/core/Map.js +1 -0
- package/src/core/MultiResolutionDataset.js +1 -0
- package/src/core/Style.js +11 -2
- package/src/core/Tooltip.js +1 -0
- package/src/dataset/COGeo.js +1 -0
- package/src/dataset/CSVGrid.js +1 -0
- package/src/dataset/GeoTIFF.js +1 -0
- package/src/dataset/JSGrid.js +1 -0
- package/src/dataset/TiledGrid.js +1 -0
- package/src/index.js +7 -3
- package/src/layer/BackgroundLayer.js +1 -0
- package/src/layer/BackgroundLayerWMS.js +1 -0
- package/src/layer/GeoJSONLayer.js +1 -0
- package/src/layer/GridLayer.js +1 -0
- package/src/layer/LabelLayer.js +1 -0
- package/src/legend/ColorCategoryLegend.js +1 -0
- package/src/legend/ColorDiscreteLegend.js +2 -0
- package/src/legend/ColorLegend.js +1 -0
- package/src/legend/OrientationLegend.js +1 -0
- package/src/legend/SizeLegend.js +4 -2
- package/src/legend/TrivariateLegend.js +210 -0
- package/src/style/CompositionStyle.js +1 -0
- package/src/style/ContourStyle.js +1 -0
- package/src/style/DotDensityStyle.js +1 -0
- package/src/style/ImageStyle.js +81 -0
- package/src/style/IsoFenceStyle.js +24 -72
- package/src/style/JoyPlotStyle.js +1 -1
- package/src/style/LegoStyle.js +22 -24
- package/src/style/MosaicStyle.js +1 -0
- package/src/style/NinjaStarStyle.js +1 -1
- package/src/style/PillarStyle.js +3 -2
- package/src/style/SegmentStyle.js +2 -1
- package/src/style/ShapeColorSizeStyle.js +29 -0
- package/src/style/SideCategoryStyle.js +1 -0
- package/src/style/SideStyle.js +50 -27
- package/src/style/SquareColorCategoryWebGLStyle.js +4 -3
- package/src/style/SquareColorWebGLStyle.js +1 -0
- package/src/style/StrokeStyle.js +48 -16
- package/src/style/TanakaStyle.js +11 -5
- package/src/style/TextStyle.js +1 -1
- package/src/style/TimeSeriesStyle.js +1 -0
- package/src/utils/chernoff.js +14 -0
- package/src/utils/scale.js +22 -11
- package/src/utils/stretching.js +4 -0
- package/src/utils/trivariate.js +203 -0
- package/src/utils/utils.js +11 -0
package/src/style/StrokeStyle.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
import { Style } from '../core/Style.js'
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
|
-
*
|
|
7
|
+
* @module style
|
|
8
8
|
* @author Julien Gaffuri
|
|
9
9
|
*/
|
|
10
10
|
export class StrokeStyle extends Style {
|
|
@@ -49,50 +49,82 @@ export class StrokeStyle extends Style {
|
|
|
49
49
|
const viewScale = this.viewScale ? this.viewScale(cells, resolution, z) : undefined
|
|
50
50
|
|
|
51
51
|
const r2 = resolution * 0.5
|
|
52
|
-
for (let
|
|
52
|
+
for (let c of cells) {
|
|
53
53
|
|
|
54
54
|
//color
|
|
55
|
-
const col = this.strokeColor ? this.strokeColor(
|
|
55
|
+
const col = this.strokeColor ? this.strokeColor(c, resolution, z, viewScale) : undefined
|
|
56
56
|
if (!col || col === 'none') continue
|
|
57
57
|
geoCanvas.ctx.strokeStyle = col
|
|
58
58
|
|
|
59
59
|
//size - in geo unit
|
|
60
|
-
const
|
|
60
|
+
const size = this.size ? this.size(c, resolution, z, viewScale) : resolution
|
|
61
61
|
|
|
62
62
|
//width
|
|
63
|
-
const wi = this.strokeWidth ? this.strokeWidth(
|
|
63
|
+
const wi = this.strokeWidth ? this.strokeWidth(c, resolution, z, viewScale) : 1 * z
|
|
64
64
|
if (!wi || wi <= 0) continue
|
|
65
65
|
geoCanvas.ctx.lineWidth = wi
|
|
66
66
|
|
|
67
67
|
//shape
|
|
68
|
-
const shape = this.shape ? this.shape(
|
|
68
|
+
const shape = this.shape ? this.shape(c, resolution, z, viewScale) : 'square'
|
|
69
69
|
if (shape === 'none') continue
|
|
70
70
|
|
|
71
71
|
//get offset
|
|
72
|
-
const offset = this.offset(
|
|
72
|
+
const offset = this.offset(c, resolution, z)
|
|
73
73
|
|
|
74
74
|
if (shape === 'square') {
|
|
75
75
|
//draw square
|
|
76
|
-
const d = resolution * (1 -
|
|
76
|
+
const d = resolution * (1 - size / resolution) * 0.5
|
|
77
77
|
geoCanvas.ctx.beginPath()
|
|
78
|
-
geoCanvas.ctx.rect(
|
|
78
|
+
geoCanvas.ctx.rect(c.x + d + offset.dx, c.y + d + offset.dy, size, size)
|
|
79
79
|
geoCanvas.ctx.stroke()
|
|
80
80
|
} else if (shape === 'circle') {
|
|
81
81
|
//draw circle
|
|
82
82
|
geoCanvas.ctx.beginPath()
|
|
83
|
-
geoCanvas.ctx.arc(
|
|
83
|
+
geoCanvas.ctx.arc(c.x + r2 + offset.dx, c.y + r2 + offset.dy, size * 0.5, 0, 2 * Math.PI, false)
|
|
84
84
|
geoCanvas.ctx.stroke()
|
|
85
85
|
} else if (shape === 'diamond') {
|
|
86
|
-
const s2 =
|
|
86
|
+
const s2 = size * 0.5
|
|
87
87
|
geoCanvas.ctx.beginPath()
|
|
88
|
-
geoCanvas.ctx.moveTo(
|
|
89
|
-
geoCanvas.ctx.lineTo(
|
|
90
|
-
geoCanvas.ctx.lineTo(
|
|
91
|
-
geoCanvas.ctx.lineTo(
|
|
92
|
-
geoCanvas.ctx.lineTo(
|
|
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
93
|
geoCanvas.ctx.stroke()
|
|
94
94
|
} else if (shape === 'donut') {
|
|
95
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()
|
|
96
128
|
} else {
|
|
97
129
|
throw new Error('Unexpected shape:' + shape)
|
|
98
130
|
}
|
package/src/style/TanakaStyle.js
CHANGED
|
@@ -8,6 +8,7 @@ import { classifier as clFun, colorClassifier as cclFun } from '../utils/scale.j
|
|
|
8
8
|
/**
|
|
9
9
|
* @see https://manifold.net/doc/mfd9/example__tanaka_contours.htm
|
|
10
10
|
*
|
|
11
|
+
* @module style
|
|
11
12
|
* @author Julien Gaffuri
|
|
12
13
|
*/
|
|
13
14
|
export class TanakaStyle {
|
|
@@ -32,15 +33,20 @@ export class TanakaStyle {
|
|
|
32
33
|
return Math.min(minWG + (sideValue - 1) * step, maxWG)
|
|
33
34
|
})
|
|
34
35
|
|
|
35
|
-
//make
|
|
36
|
+
//make classifier
|
|
36
37
|
const classifier = clFun(breaks)
|
|
37
|
-
|
|
38
|
+
//make colors table
|
|
39
|
+
const colorsDict = {};
|
|
40
|
+
for (let i = 0; i < colors.length; i++) colorsDict[i + ""] = colors[i]
|
|
38
41
|
|
|
39
|
-
const cellStyle = new SquareColorCategoryWebGLStyle({
|
|
42
|
+
const cellStyle = new SquareColorCategoryWebGLStyle({
|
|
43
|
+
code: cell => classifier(value(cell)),
|
|
44
|
+
color: colorsDict
|
|
45
|
+
})
|
|
40
46
|
|
|
41
47
|
const getSideValue = (side) => {
|
|
42
|
-
const cl1 = side.c1 ? classifier(value(side.c1)) :
|
|
43
|
-
const cl2 = side.c2 ? classifier(value(side.c2)) :
|
|
48
|
+
const cl1 = side.c1 ? classifier(value(side.c1)) : -1
|
|
49
|
+
const cl2 = side.c2 ? classifier(value(side.c2)) : -1
|
|
44
50
|
return cl1 - cl2
|
|
45
51
|
}
|
|
46
52
|
|
package/src/style/TextStyle.js
CHANGED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
//@ts-check
|
|
2
|
+
'use strict'
|
|
3
|
+
|
|
4
|
+
import { loadImage } from '../utils/utils.js'
|
|
5
|
+
|
|
6
|
+
//load chernoff images
|
|
7
|
+
export async function loadChernoffFacesImages(out, baseURL, callback = undefined) {
|
|
8
|
+
out = out || {}
|
|
9
|
+
for (let s of ["f", "m"])
|
|
10
|
+
for (let a of ["y", "m", "o"])
|
|
11
|
+
for (let i = 0; i <= 2; i++)
|
|
12
|
+
out[s + a + i] = await loadImage(baseURL + (s + a + i) + ".png")
|
|
13
|
+
if (callback) callback()
|
|
14
|
+
}
|
package/src/utils/scale.js
CHANGED
|
@@ -13,6 +13,10 @@ import { scaleQuantile } from "d3-scale"
|
|
|
13
13
|
* @typedef {function(number):string} ColorScale */
|
|
14
14
|
|
|
15
15
|
|
|
16
|
+
/**
|
|
17
|
+
* @module utils
|
|
18
|
+
*/
|
|
19
|
+
|
|
16
20
|
|
|
17
21
|
/**
|
|
18
22
|
* Generic function for view scale - continuous or discrete
|
|
@@ -29,9 +33,9 @@ export const viewScale = (opts) => {
|
|
|
29
33
|
const range_ = opts.range
|
|
30
34
|
const domain_ = opts.domain
|
|
31
35
|
const classNumber = opts.classNumber
|
|
32
|
-
return (cells,
|
|
36
|
+
return (cells, resolution, z) => {
|
|
33
37
|
const domain = domain_ || [minValue, max(cells, valueFunction)]
|
|
34
|
-
const range = range_ || [minSizePix * z,
|
|
38
|
+
const range = range_ || [minSizePix * z, resolution * maxSizeFactor]
|
|
35
39
|
const domainSize = domain[1] - domain[0], domainMin = domain[0]
|
|
36
40
|
const rangeSize = range[1] - range[0], rangeMin = range[0]
|
|
37
41
|
return t => {
|
|
@@ -61,9 +65,9 @@ export const viewScaleQuantile = (opts) => {
|
|
|
61
65
|
const minSizePix = opts.minSizePix || 1
|
|
62
66
|
const maxSizeFactor = opts.maxSizeFactor || 1
|
|
63
67
|
const scale = scaleQuantile()
|
|
64
|
-
return (cells,
|
|
68
|
+
return (cells, resolution, z) => {
|
|
65
69
|
scale.domain(cells.map(valueFunction))
|
|
66
|
-
const minSizeGeo = minSizePix * z, maxSizeGeo =
|
|
70
|
+
const minSizeGeo = minSizePix * z, maxSizeGeo = resolution * maxSizeFactor
|
|
67
71
|
scale.range(Array.from({ length: classNumber }, (_, i) => minSizeGeo + i * (maxSizeGeo - minSizeGeo) / (classNumber - 1)))
|
|
68
72
|
scale.breaks = scale.quantiles()
|
|
69
73
|
scale.values = scale.range()
|
|
@@ -87,9 +91,10 @@ export const viewScaleColor = (opts) => {
|
|
|
87
91
|
const stretching = opts.stretching
|
|
88
92
|
let colorScale = opts.colorScale || (() => "purple")
|
|
89
93
|
|
|
90
|
-
//case
|
|
91
|
-
|
|
92
|
-
|
|
94
|
+
//discrete colors case: build continuous color scale from discrete ones.
|
|
95
|
+
const nbClass = opts.colors?.length
|
|
96
|
+
if (opts.colors && nbClass)
|
|
97
|
+
colorScale = t => opts.colors[t == 1 ? nbClass - 1 : Math.floor(t * nbClass)]
|
|
93
98
|
|
|
94
99
|
return (cells) => {
|
|
95
100
|
if (cells.length == 0 || !cells) return
|
|
@@ -109,6 +114,12 @@ export const viewScaleColor = (opts) => {
|
|
|
109
114
|
if (stretching) t = stretching.invert(t)
|
|
110
115
|
return domain[0] + t * domainSize
|
|
111
116
|
}
|
|
117
|
+
//discrete colors: return the breaks
|
|
118
|
+
if (opts.colors && nbClass) {
|
|
119
|
+
scale.breaks = []
|
|
120
|
+
for (let i = 1; i < nbClass; i++)
|
|
121
|
+
scale.breaks.push(scale.invert(i / nbClass))
|
|
122
|
+
}
|
|
112
123
|
|
|
113
124
|
return scale;
|
|
114
125
|
}
|
|
@@ -145,16 +156,16 @@ export const viewScaleColorQuantile = (opts) => {
|
|
|
145
156
|
|
|
146
157
|
|
|
147
158
|
/**
|
|
148
|
-
*
|
|
159
|
+
* Combine view scale functions
|
|
149
160
|
*
|
|
150
161
|
* @param {*} obj
|
|
151
|
-
* @returns
|
|
162
|
+
* @returns {function}
|
|
152
163
|
*/
|
|
153
164
|
export const viewScaleCombination = (obj) => {
|
|
154
165
|
//obj: prop and a function to call
|
|
155
|
-
return (cells,
|
|
166
|
+
return (cells, resolution, z) => {
|
|
156
167
|
const out = {}
|
|
157
|
-
for (const p in obj) { out[p] = obj[p](cells,
|
|
168
|
+
for (const p in obj) { out[p] = obj[p](cells, resolution, z) }
|
|
158
169
|
return out
|
|
159
170
|
}
|
|
160
171
|
}
|
package/src/utils/stretching.js
CHANGED
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
//@ts-check
|
|
2
|
+
'use strict'
|
|
3
|
+
|
|
4
|
+
import { interpolateLab } from "d3-interpolate"
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
export const trivariateClassifier = (properties, totalFunction, opts = {}) => {
|
|
9
|
+
|
|
10
|
+
//the three properties
|
|
11
|
+
const p0 = properties[0], p1 = properties[1], p2 = properties[2]
|
|
12
|
+
|
|
13
|
+
//the classifier center point. sum must be equal to 1
|
|
14
|
+
const [c0, c1, c2] = opts.center || [1 / 3, 1 / 3, 1 / 3]
|
|
15
|
+
|
|
16
|
+
//parameter to decide wether to use mixed classes m0, m1, m2.
|
|
17
|
+
const withMixedClasses = opts.withMixedClasses != undefined ? opts.withMixedClasses : true
|
|
18
|
+
|
|
19
|
+
//paramerter decide wether to use a central class, and the size of this central class.
|
|
20
|
+
//set to 0 or undefined for not showing any central class. Set to 1 for a central class that contains the mix classes
|
|
21
|
+
const cc = opts.centerCoefficient ? 1 - opts.centerCoefficient : undefined
|
|
22
|
+
|
|
23
|
+
//the output classifier method
|
|
24
|
+
const fun = c => {
|
|
25
|
+
//get total
|
|
26
|
+
const tot = totalFunction(c)
|
|
27
|
+
if (!tot) return undefined
|
|
28
|
+
//compute shares
|
|
29
|
+
const [s0, s1, s2] = [+c[p0] / tot, +c[p1] / tot, +c[p2] / tot]
|
|
30
|
+
|
|
31
|
+
//class 0
|
|
32
|
+
if (s0 >= c0 && s1 <= c1 && s2 <= c2) {
|
|
33
|
+
//central class near class 0
|
|
34
|
+
if (cc != undefined && (s2 - c2) * (c1 - cc * c1) >= (s1 - cc * c1) * (cc * c2 - c2))
|
|
35
|
+
return "center"
|
|
36
|
+
return "0"
|
|
37
|
+
}
|
|
38
|
+
//class 1
|
|
39
|
+
if (s0 <= c0 && s1 >= c1 && s2 <= c2) {
|
|
40
|
+
//central class near class 1
|
|
41
|
+
if (cc != undefined && (s2 - c2) * (c0 - cc * c0) >= (s0 - cc * c0) * (cc * c2 - c2))
|
|
42
|
+
return "center"
|
|
43
|
+
return "1"
|
|
44
|
+
}
|
|
45
|
+
//class 2
|
|
46
|
+
if (s0 <= c0 && s1 <= c1 && s2 >= c2) {
|
|
47
|
+
//central class near class 2
|
|
48
|
+
if (cc != undefined && (s1 - c1) * (c0 - cc * c0) >= (s0 - cc * c0) * (cc * c1 - c1))
|
|
49
|
+
return "center"
|
|
50
|
+
return "2"
|
|
51
|
+
}
|
|
52
|
+
//middle class 0 - intersection class 1 and 2
|
|
53
|
+
if (s0 <= c0 && s1 >= c1 && s2 >= c2) {
|
|
54
|
+
//central class
|
|
55
|
+
if (cc != undefined && s0 > cc * c0) return "center"
|
|
56
|
+
if (withMixedClasses) return "m0"
|
|
57
|
+
return s1 > s2 ? "1" : "2"
|
|
58
|
+
}
|
|
59
|
+
//middle class 1 - intersection class 0 and 1
|
|
60
|
+
if (s0 >= c0 && s1 <= c1 && s2 >= c2) {
|
|
61
|
+
//central class
|
|
62
|
+
if (cc != undefined && s1 > cc * c1) return "center"
|
|
63
|
+
if (withMixedClasses) return "m1"
|
|
64
|
+
return s0 > s2 ? "0" : "2"
|
|
65
|
+
}
|
|
66
|
+
//middle class 2 - intersection class 0 and 1
|
|
67
|
+
if (s0 >= c0 && s1 >= c1 && s2 <= c2) {
|
|
68
|
+
//central class
|
|
69
|
+
if (cc != undefined && s2 > cc * c2) return "center"
|
|
70
|
+
if (withMixedClasses) return "m2"
|
|
71
|
+
return s1 > s0 ? "1" : "0"
|
|
72
|
+
}
|
|
73
|
+
//should not happen
|
|
74
|
+
return "unknown"
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
//attach information to output function
|
|
78
|
+
fun.center = [c0, c1, c2]
|
|
79
|
+
fun.centerCoefficient = opts.centerCoefficient
|
|
80
|
+
|
|
81
|
+
return fun
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
export const trivariateColorClassifier = (properties, totalFunction, colors, opts = {}) => {
|
|
87
|
+
|
|
88
|
+
//the three colors
|
|
89
|
+
const [color0, color1, color2] = colors || ["red", "green", "blue"]
|
|
90
|
+
|
|
91
|
+
//the color interpolation function
|
|
92
|
+
const colorInterpolation = opts.colorInterpolation || interpolateLab
|
|
93
|
+
|
|
94
|
+
//parameter to decide wether to use mixed classes.
|
|
95
|
+
const withMixedClasses = opts.withMixedClasses != undefined ? opts.withMixedClasses : true
|
|
96
|
+
//https://d3js.org/d3-interpolate/color
|
|
97
|
+
const mixColorFunction = (color1, color2) => colorInterpolation(color1, color2)(0.5)
|
|
98
|
+
//the colors corresponding to the mixed classes
|
|
99
|
+
const [mixColor0, mixColor1, mixColor2] = opts.mixedColors || withMixedClasses ? [mixColorFunction(color1, color2), mixColorFunction(color0, color2), mixColorFunction(color0, color1)] : []
|
|
100
|
+
|
|
101
|
+
//the central color, used for the central class, if any. The central class is the class of relatively balanced values, around the center point
|
|
102
|
+
const centerColor = opts.centerColor || colorInterpolation(mixColorFunction(color0, color1), color2)(0.333)
|
|
103
|
+
|
|
104
|
+
//make classifier
|
|
105
|
+
const classifier = trivariateClassifier(properties, totalFunction, opts)
|
|
106
|
+
|
|
107
|
+
//the output color classifier method
|
|
108
|
+
const fun = c => {
|
|
109
|
+
const cla = classifier(c)
|
|
110
|
+
if (cla == "0") return color0
|
|
111
|
+
if (cla == "1") return color1
|
|
112
|
+
if (cla == "2") return color2
|
|
113
|
+
if (cla == "m0") return mixColor0
|
|
114
|
+
if (cla == "m1") return mixColor1
|
|
115
|
+
if (cla == "m2") return mixColor2
|
|
116
|
+
if (cla == "center") return centerColor
|
|
117
|
+
return opts.defaultColor || "black"
|
|
118
|
+
}
|
|
119
|
+
fun.center = classifier.center
|
|
120
|
+
fun.centerCoefficient = opts.centerCoefficient
|
|
121
|
+
fun.colors = [color0, color1, color2]
|
|
122
|
+
fun.mixColors = [mixColor0, mixColor1, mixColor2]
|
|
123
|
+
fun.centerColor = centerColor
|
|
124
|
+
|
|
125
|
+
return fun
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
/*
|
|
134
|
+
const orderedIndexesDec = arr => [...arr.keys()].sort((a, b) => arr[b] - arr[a]);
|
|
135
|
+
const orderedIndexesInc = arr => [...arr.keys()].sort((a, b) => arr[a] - arr[b]);
|
|
136
|
+
|
|
137
|
+
export const trivariateClassifier = (properties, totalFunction, opts = {}) => {
|
|
138
|
+
const lowThreshold = opts.lowThreshold || [1 / 3, 1 / 3, 1 / 3]
|
|
139
|
+
const highThreshold = opts.highThreshold || [2 / 3, 2 / 3, 2 / 3]
|
|
140
|
+
const colors = opts.colors || ["red", "green", "blue"]
|
|
141
|
+
const colorInterpolation = opts.colorInterpolation || interpolateLab
|
|
142
|
+
|
|
143
|
+
//https://d3js.org/d3-interpolate/color
|
|
144
|
+
const middleColorFunction = (color1, color2) => colorInterpolation(color1, color2)(0.5)
|
|
145
|
+
const middleColors = opts.middleColors || [middleColorFunction(colors[1], colors[2]), middleColorFunction(colors[0], colors[2]), middleColorFunction(colors[0], colors[1])]
|
|
146
|
+
const centerColor = opts.centerColor || colorInterpolation(middleColors[2], colors[2])(0.333)
|
|
147
|
+
|
|
148
|
+
const high_ = orderedIndexesDec(highThreshold)
|
|
149
|
+
const low_ = orderedIndexesInc(lowThreshold)
|
|
150
|
+
|
|
151
|
+
const p0 = properties[0], p1 = properties[1], p2 = properties[2]
|
|
152
|
+
const fun = c => {
|
|
153
|
+
//get total
|
|
154
|
+
const tot = totalFunction(c)
|
|
155
|
+
if (!tot) return undefined
|
|
156
|
+
//compute shares
|
|
157
|
+
const shares = [+c[p0] / tot, +c[p1] / tot, +c[p2] / tot]
|
|
158
|
+
//return colors
|
|
159
|
+
//start first with the extreme high (triangles): from the larger value (small triangle) to the lower value (large triangle)
|
|
160
|
+
for (let i of high_) if (shares[i] > highThreshold[i]) return colors[i]
|
|
161
|
+
//then draw the extreme low (trapeziums): from the lower value (small trapeziums) to the larger values (large trapezium)
|
|
162
|
+
for (let i of low_) if (shares[i] < lowThreshold[i]) return middleColors[i]
|
|
163
|
+
//else central color
|
|
164
|
+
return centerColor
|
|
165
|
+
}
|
|
166
|
+
fun.lowThreshold = lowThreshold
|
|
167
|
+
fun.highThreshold = highThreshold
|
|
168
|
+
fun.colors = colors
|
|
169
|
+
fun.middleColors = middleColors
|
|
170
|
+
fun.centerColor = centerColor
|
|
171
|
+
fun.lowIndex = low_
|
|
172
|
+
fun.highIndex = high_
|
|
173
|
+
return fun
|
|
174
|
+
}
|
|
175
|
+
*/
|
|
176
|
+
|
|
177
|
+
/*
|
|
178
|
+
export const trivariateClassifier3 = (properties, totalFunction, opts = {}) => {
|
|
179
|
+
const [a01, a12, a20] = opts.thresholds || [1/3, 1/3, 1/3]
|
|
180
|
+
const [c0, c1, c2] = opts.colors || ["red", "green", "blue"]
|
|
181
|
+
const centerColor = opts.centerColor || "gray"
|
|
182
|
+
|
|
183
|
+
const fff = a => a == 1 ? Infinity : a / (1 - a)
|
|
184
|
+
const c01 = fff(a01), c12 = fff(a12), c20 = fff(a20)
|
|
185
|
+
|
|
186
|
+
const p0 = properties[0], p1 = properties[1], p2 = properties[2]
|
|
187
|
+
const fun = c => {
|
|
188
|
+
//get total
|
|
189
|
+
const tot = totalFunction(c)
|
|
190
|
+
if (!tot) return undefined
|
|
191
|
+
//compute shares
|
|
192
|
+
const [s0, s1, s2] = [+c[p0] / tot, +c[p1] / tot, +c[p2] / tot]
|
|
193
|
+
//return colors
|
|
194
|
+
if (s0 * c01 > s1 && s0 > s2 * c20) return c0
|
|
195
|
+
else if (s1 > s0 * c01 && s1 * c12 > s2) return c1
|
|
196
|
+
else if (s2 * c20 > s0 && s2 > s1 * c12) return c2
|
|
197
|
+
else return centerColor
|
|
198
|
+
}
|
|
199
|
+
fun.colors = [c0, c1, c2]
|
|
200
|
+
fun.centerColor = centerColor
|
|
201
|
+
return fun
|
|
202
|
+
}
|
|
203
|
+
*/
|
package/src/utils/utils.js
CHANGED
|
@@ -29,8 +29,18 @@ export function nice(v, multiples = [8, 6, 5, 4, 2.5, 2]) {
|
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
|
|
32
|
+
export function loadImage(src) {
|
|
33
|
+
return new Promise((resolve, reject) => {
|
|
34
|
+
const img = new Image();
|
|
35
|
+
img.onload = function () { resolve(img); };
|
|
36
|
+
img.onerror = function () { reject(new Error('Error loading image')); };
|
|
37
|
+
img.src = src;
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
|
|
32
41
|
|
|
33
42
|
|
|
43
|
+
/*
|
|
34
44
|
export let monitor = false
|
|
35
45
|
|
|
36
46
|
let previousDate
|
|
@@ -48,3 +58,4 @@ export function monitorDuration(message) {
|
|
|
48
58
|
previousDate = nowDate
|
|
49
59
|
console.log(d, message)
|
|
50
60
|
}
|
|
61
|
+
*/
|