gridviz 3.0.22 → 3.0.24
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 +128 -128
- package/dist/gridviz.js +8618 -8579
- package/dist/gridviz.min.js +1 -1
- package/package.json +74 -74
- package/src/button/Button.js +67 -67
- package/src/button/FullscreenButton.js +121 -121
- package/src/button/ZoomButtons.js +103 -101
- package/src/core/Dataset.js +154 -154
- package/src/core/Drawable.js +78 -78
- package/src/core/GeoCanvas.js +437 -430
- package/src/core/Layer.js +23 -23
- package/src/core/Legend.js +108 -108
- package/src/core/Map.js +542 -516
- package/src/core/MultiResolutionDataset.js +93 -93
- package/src/core/Style.js +98 -98
- package/src/core/Tooltip.js +182 -182
- package/src/dataset/COGeo.js +25 -25
- package/src/dataset/CSVGrid.js +112 -112
- package/src/dataset/GeoTIFF.js +122 -122
- package/src/dataset/JSGrid.js +55 -55
- package/src/dataset/TiledGrid.js +302 -302
- package/src/dataset/worker.js +18 -18
- package/src/index.js +77 -77
- package/src/layer/BackgroundLayer.js +173 -173
- package/src/layer/BackgroundLayerImage.js +81 -81
- package/src/layer/BackgroundLayerWMS.js +104 -104
- package/src/layer/GeoJSONLayer.js +214 -214
- package/src/layer/GridLayer.js +148 -148
- package/src/layer/LabelLayer.js +194 -194
- package/src/legend/ColorCategoryLegend.js +109 -109
- package/src/legend/ColorDiscreteLegend.js +100 -100
- package/src/legend/ColorLegend.js +115 -115
- package/src/legend/OrientationLegend.js +89 -89
- package/src/legend/SizeLegend.js +224 -224
- package/src/legend/TernaryLegend.js +302 -302
- package/src/style/CompositionStyle.js +291 -291
- package/src/style/ContourStyle.js +21 -21
- package/src/style/DotDensityStyle.js +133 -133
- package/src/style/ImageStyle.js +100 -100
- package/src/style/IsoFenceStyle.js +217 -217
- package/src/style/JoyPlotStyle.js +127 -127
- package/src/style/LegoStyle.js +110 -110
- package/src/style/MosaicStyle.js +99 -99
- package/src/style/NinjaStarStyle.js +102 -102
- package/src/style/PillarStyle.js +207 -207
- package/src/style/SegmentStyle.js +105 -105
- package/src/style/ShapeColorSizeStyle.js +147 -147
- package/src/style/SideCategoryStyle.js +112 -112
- package/src/style/SideStyle.js +212 -212
- package/src/style/SquareColorCategoryWebGLStyle.js +107 -107
- package/src/style/SquareColorWebGLStyle.js +126 -126
- package/src/style/StrokeStyle.js +135 -135
- package/src/style/TanakaStyle.js +69 -69
- package/src/style/TextStyle.js +110 -110
- package/src/style/TimeSeriesStyle.js +222 -222
- package/src/utils/WebGLRectangleColoring.js +106 -106
- package/src/utils/WebGLSquareColoring.js +118 -118
- package/src/utils/WebGLSquareColoringAdvanced.js +230 -230
- package/src/utils/WebGLSquareColoringCatAdvanced.js +133 -133
- package/src/utils/WebGLSquareColoringKS.js +153 -153
- package/src/utils/scale.js +218 -218
- package/src/utils/stretching.js +120 -120
- package/src/utils/ternary.js +208 -208
- package/src/utils/utils.js +58 -58
- package/src/utils/webGLUtils.js +72 -72
- package/src/utils/zoomUtils.js +142 -142
|
@@ -1,291 +1,291 @@
|
|
|
1
|
-
//@ts-check
|
|
2
|
-
'use strict'
|
|
3
|
-
|
|
4
|
-
import { Style } from '../core/Style.js'
|
|
5
|
-
|
|
6
|
-
/** @typedef {"flag"|"piechart"|"ring"|"segment"|"radar"|"agepyramid"|"halftone"} CompositionType */
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* A style showing the composition of a total in different categories, with different color hues.
|
|
10
|
-
* It consists of a symbol with different parts, whose size reflect the proportion of the corresponding category.
|
|
11
|
-
* For a list of supported symbols, @see CompositionType
|
|
12
|
-
* The symbol can be scaled depending on the cell importance.
|
|
13
|
-
*
|
|
14
|
-
* @module style
|
|
15
|
-
* @author Julien Gaffuri
|
|
16
|
-
*/
|
|
17
|
-
export class CompositionStyle extends Style {
|
|
18
|
-
/** @param {object} opts */
|
|
19
|
-
constructor(opts) {
|
|
20
|
-
super(opts)
|
|
21
|
-
opts = opts || {}
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* The dictionary (string -> color) which give the color of each category.
|
|
25
|
-
* @type {object} */
|
|
26
|
-
this.color = opts.color
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* A function returning the type of decomposition symbol of a cell, @see CompositionType
|
|
30
|
-
* @type {function(import("../core/Dataset.js").Cell,number, number,object):CompositionType} */
|
|
31
|
-
this.type = opts.type || (() => 'flag') //(c,r,z,vs) => {}
|
|
32
|
-
|
|
33
|
-
/** A function returning the size of a cell in geographical unit.
|
|
34
|
-
* @type {function(import('../core/Dataset.js').Cell, number, number, object):number} */
|
|
35
|
-
this.size = opts.size || ((c, r) => r) //(c,r,z,vs) => {}
|
|
36
|
-
|
|
37
|
-
/** For style types with stripes (flag, segment), the orientation of the stripes (0 for horizontal, other for vertical).
|
|
38
|
-
* @type {function(import("../core/Dataset.js").Cell,number,number,object):number} */
|
|
39
|
-
this.stripesOrientation = opts.stripesOrientation || (() => 0) //(c,r,z,vs) => ...
|
|
40
|
-
|
|
41
|
-
/** The function specifying an offset angle for a radar, halftone or pie chart style.
|
|
42
|
-
* The angle is specified in degree. The rotation is anti-clockwise.
|
|
43
|
-
* @type {function(import("../core/Dataset.js").Cell,number,number,object):number} */
|
|
44
|
-
this.offsetAngle = opts.offsetAngle || (() => 0) //(c,r,z,vs) => ...
|
|
45
|
-
|
|
46
|
-
/** The function specifying the height of the age pyramid, in geo unit.
|
|
47
|
-
* @type {function(import("../core/Dataset.js").Cell,number,number,object):number} */
|
|
48
|
-
this.agePyramidHeight = opts.agePyramidHeight || ((c, r) => r) //(c,r,z,vs) => ...
|
|
49
|
-
|
|
50
|
-
/** For pie chart, this is parameter for internal radius, so that the pie chart looks like a donut.
|
|
51
|
-
* 0 for normal pie charts, 0.5 to empty half of the radius.
|
|
52
|
-
* @type {number} */
|
|
53
|
-
this.pieChartInternalRadiusFactor = opts.pieChartInternalRadiusFactor || 0
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* Draw cells as squares depending on their value.
|
|
58
|
-
*
|
|
59
|
-
* @param {Array.<import("../core/Dataset.js").Cell>} cells
|
|
60
|
-
* @param {import("../core/GeoCanvas.js").GeoCanvas} geoCanvas
|
|
61
|
-
* @param {number} resolution
|
|
62
|
-
*/
|
|
63
|
-
draw(cells, geoCanvas, resolution) {
|
|
64
|
-
//filter
|
|
65
|
-
if (this.filter) cells = cells.filter(this.filter)
|
|
66
|
-
|
|
67
|
-
//
|
|
68
|
-
const z = geoCanvas.view.z
|
|
69
|
-
const ctx = geoCanvas.offscreenCtx
|
|
70
|
-
|
|
71
|
-
//get view scale
|
|
72
|
-
const viewScale = this.viewScale ? this.viewScale(cells, resolution, z) : undefined
|
|
73
|
-
|
|
74
|
-
//nb categories - used for radar and agepyramid
|
|
75
|
-
const nbCat = Object.entries(this.color).length
|
|
76
|
-
|
|
77
|
-
//draw calls
|
|
78
|
-
for (let cell of cells) {
|
|
79
|
-
//size
|
|
80
|
-
const sG = this.size ? this.size(cell, resolution, z, viewScale) : resolution
|
|
81
|
-
if (!sG) continue
|
|
82
|
-
|
|
83
|
-
//get offset
|
|
84
|
-
const offset = this.offset(cell, resolution, z)
|
|
85
|
-
|
|
86
|
-
//get symbol type
|
|
87
|
-
const type_ = this.type ? this.type(cell, resolution, z, viewScale) : 'flag'
|
|
88
|
-
|
|
89
|
-
//compute center position
|
|
90
|
-
const xc = cell.x + offset.dx + (type_ === 'agepyramid' ? 0 : resolution * 0.5)
|
|
91
|
-
const yc = cell.y + offset.dy + (type_ === 'agepyramid' ? 0 : resolution * 0.5)
|
|
92
|
-
|
|
93
|
-
//compute offset angle, when relevant
|
|
94
|
-
const offAng = this.offsetAngle
|
|
95
|
-
? (this.offsetAngle(cell, resolution, z, viewScale) * Math.PI) / 180
|
|
96
|
-
: 0
|
|
97
|
-
|
|
98
|
-
if (type_ === 'agepyramid' || type_ === 'radar' || type_ === 'halftone') {
|
|
99
|
-
//get cell category max value
|
|
100
|
-
let maxVal = -Infinity
|
|
101
|
-
for (let key of Object.keys(this.color)) {
|
|
102
|
-
const v = +cell[key]
|
|
103
|
-
if (v > maxVal) maxVal = v
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
//cumul
|
|
107
|
-
let cumul = 0
|
|
108
|
-
if (type_ === 'agepyramid' && this.agePyramidHeight)
|
|
109
|
-
cumul = (resolution - this.agePyramidHeight(cell, resolution, z, viewScale)) / 2
|
|
110
|
-
if (type_ === 'radar' || type_ === 'halftone') cumul = Math.PI / 2 + offAng
|
|
111
|
-
|
|
112
|
-
//compute the increment, which is the value to increment the cumul for each category
|
|
113
|
-
const incr =
|
|
114
|
-
type_ === 'agepyramid'
|
|
115
|
-
? (this.agePyramidHeight
|
|
116
|
-
? this.agePyramidHeight(cell, resolution, z, viewScale)
|
|
117
|
-
: resolution) / nbCat
|
|
118
|
-
: type_ === 'radar' || type_ === 'halftone'
|
|
119
|
-
? (2 * Math.PI) / nbCat
|
|
120
|
-
: undefined
|
|
121
|
-
if (incr === undefined) throw new Error('Unexpected symbol type:' + type_)
|
|
122
|
-
|
|
123
|
-
for (let [column, color] of Object.entries(this.color)) {
|
|
124
|
-
if (type_ === 'agepyramid') {
|
|
125
|
-
//set category color
|
|
126
|
-
ctx.fillStyle = color
|
|
127
|
-
|
|
128
|
-
//get category value
|
|
129
|
-
const val = cell[column]
|
|
130
|
-
|
|
131
|
-
//compute category length - in geo
|
|
132
|
-
/** @type {number} */
|
|
133
|
-
const wG = (sG * val) / maxVal
|
|
134
|
-
|
|
135
|
-
//draw bar
|
|
136
|
-
ctx.fillRect(xc + (resolution - wG) / 2, yc + cumul, wG, incr)
|
|
137
|
-
|
|
138
|
-
//next height
|
|
139
|
-
cumul += incr
|
|
140
|
-
} else if (type_ === 'radar') {
|
|
141
|
-
//set category color
|
|
142
|
-
ctx.fillStyle = color
|
|
143
|
-
|
|
144
|
-
//get categroy value
|
|
145
|
-
const val = cell[column]
|
|
146
|
-
|
|
147
|
-
//compute category radius - in geo
|
|
148
|
-
/** @type {number} */
|
|
149
|
-
//const rG = this.radius(val, r, stat, cellStat, z)
|
|
150
|
-
const rG = (sG / 2) * Math.sqrt(val / maxVal)
|
|
151
|
-
|
|
152
|
-
//draw angular sector
|
|
153
|
-
ctx.beginPath()
|
|
154
|
-
ctx.moveTo(xc, yc)
|
|
155
|
-
ctx.arc(xc, yc, rG, cumul - incr, cumul)
|
|
156
|
-
ctx.lineTo(xc, yc)
|
|
157
|
-
ctx.fill()
|
|
158
|
-
|
|
159
|
-
//next angular sector
|
|
160
|
-
cumul += incr
|
|
161
|
-
} else if (type_ === 'halftone') {
|
|
162
|
-
//set category color
|
|
163
|
-
ctx.fillStyle = color
|
|
164
|
-
|
|
165
|
-
//get categroy value
|
|
166
|
-
const val = cell[column]
|
|
167
|
-
|
|
168
|
-
//compute category radius - in geo
|
|
169
|
-
/** @type {number} */
|
|
170
|
-
const rG = sG * 0.333 * Math.sqrt(val / maxVal)
|
|
171
|
-
|
|
172
|
-
//draw circle
|
|
173
|
-
ctx.beginPath()
|
|
174
|
-
ctx.arc(
|
|
175
|
-
xc + resolution * 0.25 * Math.cos(cumul),
|
|
176
|
-
yc + resolution * 0.25 * Math.sin(cumul),
|
|
177
|
-
rG,
|
|
178
|
-
0,
|
|
179
|
-
2 * Math.PI
|
|
180
|
-
)
|
|
181
|
-
ctx.fill()
|
|
182
|
-
|
|
183
|
-
//next angular sector
|
|
184
|
-
cumul += incr
|
|
185
|
-
} else {
|
|
186
|
-
throw new Error('Unexpected symbol type:' + type_)
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
} else {
|
|
190
|
-
//compute total
|
|
191
|
-
let total = 0
|
|
192
|
-
for (let column of Object.keys(this.color)) {
|
|
193
|
-
const v = +cell[column]
|
|
194
|
-
if (!v) continue
|
|
195
|
-
total += v
|
|
196
|
-
}
|
|
197
|
-
if (!total || isNaN(total)) continue
|
|
198
|
-
|
|
199
|
-
//draw decomposition symbol
|
|
200
|
-
let cumul = 0
|
|
201
|
-
const d = resolution * (1 - sG / resolution) * 0.5
|
|
202
|
-
const ori = this.stripesOrientation(cell, resolution, z, viewScale)
|
|
203
|
-
|
|
204
|
-
for (let [column, color] of Object.entries(this.color)) {
|
|
205
|
-
//get share
|
|
206
|
-
const share = cell[column] / total
|
|
207
|
-
if (!share || isNaN(share)) continue
|
|
208
|
-
|
|
209
|
-
//set color
|
|
210
|
-
ctx.fillStyle = color
|
|
211
|
-
|
|
212
|
-
//draw symbol part
|
|
213
|
-
if (type_ === 'flag') {
|
|
214
|
-
//draw flag stripe
|
|
215
|
-
if (ori == 0) {
|
|
216
|
-
//horizontal
|
|
217
|
-
ctx.fillRect(
|
|
218
|
-
cell.x + d + offset.dx,
|
|
219
|
-
cell.y + d + cumul * sG + offset.dy,
|
|
220
|
-
sG,
|
|
221
|
-
share * sG
|
|
222
|
-
)
|
|
223
|
-
} else {
|
|
224
|
-
//vertical
|
|
225
|
-
ctx.fillRect(
|
|
226
|
-
cell.x + d + cumul * sG + offset.dx,
|
|
227
|
-
cell.y + d + offset.dy,
|
|
228
|
-
share * sG,
|
|
229
|
-
sG
|
|
230
|
-
)
|
|
231
|
-
}
|
|
232
|
-
} else if (type_ === 'piechart') {
|
|
233
|
-
//draw pie chart angular sector
|
|
234
|
-
|
|
235
|
-
//compute angles
|
|
236
|
-
const a1 = cumul * 2 * Math.PI
|
|
237
|
-
const a2 = (cumul + share) * 2 * Math.PI
|
|
238
|
-
|
|
239
|
-
//draw
|
|
240
|
-
ctx.beginPath()
|
|
241
|
-
ctx.moveTo(xc, yc)
|
|
242
|
-
ctx.arc(xc, yc, sG * 0.5, a1 + offAng, a2 + offAng)
|
|
243
|
-
if (this.pieChartInternalRadiusFactor)
|
|
244
|
-
ctx.arc(
|
|
245
|
-
xc,
|
|
246
|
-
yc,
|
|
247
|
-
sG * 0.5 * this.pieChartInternalRadiusFactor,
|
|
248
|
-
a1 + offAng,
|
|
249
|
-
a2 + offAng,
|
|
250
|
-
true
|
|
251
|
-
)
|
|
252
|
-
ctx.closePath()
|
|
253
|
-
ctx.fill()
|
|
254
|
-
} else if (type_ === 'ring') {
|
|
255
|
-
//draw ring
|
|
256
|
-
ctx.beginPath()
|
|
257
|
-
ctx.arc(xc, yc, Math.sqrt(1 - cumul) * sG * 0.5, 0, 2 * Math.PI)
|
|
258
|
-
ctx.fill()
|
|
259
|
-
} else if (type_ === 'segment') {
|
|
260
|
-
//draw segment sections
|
|
261
|
-
const wG = (sG * sG) / resolution
|
|
262
|
-
if (ori == 0) {
|
|
263
|
-
//horizontal
|
|
264
|
-
ctx.fillRect(
|
|
265
|
-
cell.x + offset.dx,
|
|
266
|
-
cell.y + (resolution - wG) / 2 + cumul * wG + offset.dy,
|
|
267
|
-
resolution,
|
|
268
|
-
share * wG
|
|
269
|
-
)
|
|
270
|
-
} else {
|
|
271
|
-
//vertical
|
|
272
|
-
ctx.fillRect(
|
|
273
|
-
cell.x + cumul * resolution + offset.dx,
|
|
274
|
-
cell.y + (resolution - wG) / 2 + offset.dy,
|
|
275
|
-
share * resolution,
|
|
276
|
-
wG
|
|
277
|
-
)
|
|
278
|
-
}
|
|
279
|
-
} else {
|
|
280
|
-
throw new Error('Unexpected symbol type:' + type_)
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
cumul += share
|
|
284
|
-
}
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
//update legends
|
|
289
|
-
this.updateLegends({ style: this, resolution: resolution, z: z, viewScale: viewScale })
|
|
290
|
-
}
|
|
291
|
-
}
|
|
1
|
+
//@ts-check
|
|
2
|
+
'use strict'
|
|
3
|
+
|
|
4
|
+
import { Style } from '../core/Style.js'
|
|
5
|
+
|
|
6
|
+
/** @typedef {"flag"|"piechart"|"ring"|"segment"|"radar"|"agepyramid"|"halftone"} CompositionType */
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* A style showing the composition of a total in different categories, with different color hues.
|
|
10
|
+
* It consists of a symbol with different parts, whose size reflect the proportion of the corresponding category.
|
|
11
|
+
* For a list of supported symbols, @see CompositionType
|
|
12
|
+
* The symbol can be scaled depending on the cell importance.
|
|
13
|
+
*
|
|
14
|
+
* @module style
|
|
15
|
+
* @author Julien Gaffuri
|
|
16
|
+
*/
|
|
17
|
+
export class CompositionStyle extends Style {
|
|
18
|
+
/** @param {object} opts */
|
|
19
|
+
constructor(opts) {
|
|
20
|
+
super(opts)
|
|
21
|
+
opts = opts || {}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* The dictionary (string -> color) which give the color of each category.
|
|
25
|
+
* @type {object} */
|
|
26
|
+
this.color = opts.color
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* A function returning the type of decomposition symbol of a cell, @see CompositionType
|
|
30
|
+
* @type {function(import("../core/Dataset.js").Cell,number, number,object):CompositionType} */
|
|
31
|
+
this.type = opts.type || (() => 'flag') //(c,r,z,vs) => {}
|
|
32
|
+
|
|
33
|
+
/** A function returning the size of a cell in geographical unit.
|
|
34
|
+
* @type {function(import('../core/Dataset.js').Cell, number, number, object):number} */
|
|
35
|
+
this.size = opts.size || ((c, r) => r) //(c,r,z,vs) => {}
|
|
36
|
+
|
|
37
|
+
/** For style types with stripes (flag, segment), the orientation of the stripes (0 for horizontal, other for vertical).
|
|
38
|
+
* @type {function(import("../core/Dataset.js").Cell,number,number,object):number} */
|
|
39
|
+
this.stripesOrientation = opts.stripesOrientation || (() => 0) //(c,r,z,vs) => ...
|
|
40
|
+
|
|
41
|
+
/** The function specifying an offset angle for a radar, halftone or pie chart style.
|
|
42
|
+
* The angle is specified in degree. The rotation is anti-clockwise.
|
|
43
|
+
* @type {function(import("../core/Dataset.js").Cell,number,number,object):number} */
|
|
44
|
+
this.offsetAngle = opts.offsetAngle || (() => 0) //(c,r,z,vs) => ...
|
|
45
|
+
|
|
46
|
+
/** The function specifying the height of the age pyramid, in geo unit.
|
|
47
|
+
* @type {function(import("../core/Dataset.js").Cell,number,number,object):number} */
|
|
48
|
+
this.agePyramidHeight = opts.agePyramidHeight || ((c, r) => r) //(c,r,z,vs) => ...
|
|
49
|
+
|
|
50
|
+
/** For pie chart, this is parameter for internal radius, so that the pie chart looks like a donut.
|
|
51
|
+
* 0 for normal pie charts, 0.5 to empty half of the radius.
|
|
52
|
+
* @type {number} */
|
|
53
|
+
this.pieChartInternalRadiusFactor = opts.pieChartInternalRadiusFactor || 0
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Draw cells as squares depending on their value.
|
|
58
|
+
*
|
|
59
|
+
* @param {Array.<import("../core/Dataset.js").Cell>} cells
|
|
60
|
+
* @param {import("../core/GeoCanvas.js").GeoCanvas} geoCanvas
|
|
61
|
+
* @param {number} resolution
|
|
62
|
+
*/
|
|
63
|
+
draw(cells, geoCanvas, resolution) {
|
|
64
|
+
//filter
|
|
65
|
+
if (this.filter) cells = cells.filter(this.filter)
|
|
66
|
+
|
|
67
|
+
//
|
|
68
|
+
const z = geoCanvas.view.z
|
|
69
|
+
const ctx = geoCanvas.offscreenCtx
|
|
70
|
+
|
|
71
|
+
//get view scale
|
|
72
|
+
const viewScale = this.viewScale ? this.viewScale(cells, resolution, z) : undefined
|
|
73
|
+
|
|
74
|
+
//nb categories - used for radar and agepyramid
|
|
75
|
+
const nbCat = Object.entries(this.color).length
|
|
76
|
+
|
|
77
|
+
//draw calls
|
|
78
|
+
for (let cell of cells) {
|
|
79
|
+
//size
|
|
80
|
+
const sG = this.size ? this.size(cell, resolution, z, viewScale) : resolution
|
|
81
|
+
if (!sG) continue
|
|
82
|
+
|
|
83
|
+
//get offset
|
|
84
|
+
const offset = this.offset(cell, resolution, z)
|
|
85
|
+
|
|
86
|
+
//get symbol type
|
|
87
|
+
const type_ = this.type ? this.type(cell, resolution, z, viewScale) : 'flag'
|
|
88
|
+
|
|
89
|
+
//compute center position
|
|
90
|
+
const xc = cell.x + offset.dx + (type_ === 'agepyramid' ? 0 : resolution * 0.5)
|
|
91
|
+
const yc = cell.y + offset.dy + (type_ === 'agepyramid' ? 0 : resolution * 0.5)
|
|
92
|
+
|
|
93
|
+
//compute offset angle, when relevant
|
|
94
|
+
const offAng = this.offsetAngle
|
|
95
|
+
? (this.offsetAngle(cell, resolution, z, viewScale) * Math.PI) / 180
|
|
96
|
+
: 0
|
|
97
|
+
|
|
98
|
+
if (type_ === 'agepyramid' || type_ === 'radar' || type_ === 'halftone') {
|
|
99
|
+
//get cell category max value
|
|
100
|
+
let maxVal = -Infinity
|
|
101
|
+
for (let key of Object.keys(this.color)) {
|
|
102
|
+
const v = +cell[key]
|
|
103
|
+
if (v > maxVal) maxVal = v
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
//cumul
|
|
107
|
+
let cumul = 0
|
|
108
|
+
if (type_ === 'agepyramid' && this.agePyramidHeight)
|
|
109
|
+
cumul = (resolution - this.agePyramidHeight(cell, resolution, z, viewScale)) / 2
|
|
110
|
+
if (type_ === 'radar' || type_ === 'halftone') cumul = Math.PI / 2 + offAng
|
|
111
|
+
|
|
112
|
+
//compute the increment, which is the value to increment the cumul for each category
|
|
113
|
+
const incr =
|
|
114
|
+
type_ === 'agepyramid'
|
|
115
|
+
? (this.agePyramidHeight
|
|
116
|
+
? this.agePyramidHeight(cell, resolution, z, viewScale)
|
|
117
|
+
: resolution) / nbCat
|
|
118
|
+
: type_ === 'radar' || type_ === 'halftone'
|
|
119
|
+
? (2 * Math.PI) / nbCat
|
|
120
|
+
: undefined
|
|
121
|
+
if (incr === undefined) throw new Error('Unexpected symbol type:' + type_)
|
|
122
|
+
|
|
123
|
+
for (let [column, color] of Object.entries(this.color)) {
|
|
124
|
+
if (type_ === 'agepyramid') {
|
|
125
|
+
//set category color
|
|
126
|
+
ctx.fillStyle = color
|
|
127
|
+
|
|
128
|
+
//get category value
|
|
129
|
+
const val = cell[column]
|
|
130
|
+
|
|
131
|
+
//compute category length - in geo
|
|
132
|
+
/** @type {number} */
|
|
133
|
+
const wG = (sG * val) / maxVal
|
|
134
|
+
|
|
135
|
+
//draw bar
|
|
136
|
+
ctx.fillRect(xc + (resolution - wG) / 2, yc + cumul, wG, incr)
|
|
137
|
+
|
|
138
|
+
//next height
|
|
139
|
+
cumul += incr
|
|
140
|
+
} else if (type_ === 'radar') {
|
|
141
|
+
//set category color
|
|
142
|
+
ctx.fillStyle = color
|
|
143
|
+
|
|
144
|
+
//get categroy value
|
|
145
|
+
const val = cell[column]
|
|
146
|
+
|
|
147
|
+
//compute category radius - in geo
|
|
148
|
+
/** @type {number} */
|
|
149
|
+
//const rG = this.radius(val, r, stat, cellStat, z)
|
|
150
|
+
const rG = (sG / 2) * Math.sqrt(val / maxVal)
|
|
151
|
+
|
|
152
|
+
//draw angular sector
|
|
153
|
+
ctx.beginPath()
|
|
154
|
+
ctx.moveTo(xc, yc)
|
|
155
|
+
ctx.arc(xc, yc, rG, cumul - incr, cumul)
|
|
156
|
+
ctx.lineTo(xc, yc)
|
|
157
|
+
ctx.fill()
|
|
158
|
+
|
|
159
|
+
//next angular sector
|
|
160
|
+
cumul += incr
|
|
161
|
+
} else if (type_ === 'halftone') {
|
|
162
|
+
//set category color
|
|
163
|
+
ctx.fillStyle = color
|
|
164
|
+
|
|
165
|
+
//get categroy value
|
|
166
|
+
const val = cell[column]
|
|
167
|
+
|
|
168
|
+
//compute category radius - in geo
|
|
169
|
+
/** @type {number} */
|
|
170
|
+
const rG = sG * 0.333 * Math.sqrt(val / maxVal)
|
|
171
|
+
|
|
172
|
+
//draw circle
|
|
173
|
+
ctx.beginPath()
|
|
174
|
+
ctx.arc(
|
|
175
|
+
xc + resolution * 0.25 * Math.cos(cumul),
|
|
176
|
+
yc + resolution * 0.25 * Math.sin(cumul),
|
|
177
|
+
rG,
|
|
178
|
+
0,
|
|
179
|
+
2 * Math.PI
|
|
180
|
+
)
|
|
181
|
+
ctx.fill()
|
|
182
|
+
|
|
183
|
+
//next angular sector
|
|
184
|
+
cumul += incr
|
|
185
|
+
} else {
|
|
186
|
+
throw new Error('Unexpected symbol type:' + type_)
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
} else {
|
|
190
|
+
//compute total
|
|
191
|
+
let total = 0
|
|
192
|
+
for (let column of Object.keys(this.color)) {
|
|
193
|
+
const v = +cell[column]
|
|
194
|
+
if (!v) continue
|
|
195
|
+
total += v
|
|
196
|
+
}
|
|
197
|
+
if (!total || isNaN(total)) continue
|
|
198
|
+
|
|
199
|
+
//draw decomposition symbol
|
|
200
|
+
let cumul = 0
|
|
201
|
+
const d = resolution * (1 - sG / resolution) * 0.5
|
|
202
|
+
const ori = this.stripesOrientation(cell, resolution, z, viewScale)
|
|
203
|
+
|
|
204
|
+
for (let [column, color] of Object.entries(this.color)) {
|
|
205
|
+
//get share
|
|
206
|
+
const share = cell[column] / total
|
|
207
|
+
if (!share || isNaN(share)) continue
|
|
208
|
+
|
|
209
|
+
//set color
|
|
210
|
+
ctx.fillStyle = color
|
|
211
|
+
|
|
212
|
+
//draw symbol part
|
|
213
|
+
if (type_ === 'flag') {
|
|
214
|
+
//draw flag stripe
|
|
215
|
+
if (ori == 0) {
|
|
216
|
+
//horizontal
|
|
217
|
+
ctx.fillRect(
|
|
218
|
+
cell.x + d + offset.dx,
|
|
219
|
+
cell.y + d + cumul * sG + offset.dy,
|
|
220
|
+
sG,
|
|
221
|
+
share * sG
|
|
222
|
+
)
|
|
223
|
+
} else {
|
|
224
|
+
//vertical
|
|
225
|
+
ctx.fillRect(
|
|
226
|
+
cell.x + d + cumul * sG + offset.dx,
|
|
227
|
+
cell.y + d + offset.dy,
|
|
228
|
+
share * sG,
|
|
229
|
+
sG
|
|
230
|
+
)
|
|
231
|
+
}
|
|
232
|
+
} else if (type_ === 'piechart') {
|
|
233
|
+
//draw pie chart angular sector
|
|
234
|
+
|
|
235
|
+
//compute angles
|
|
236
|
+
const a1 = cumul * 2 * Math.PI
|
|
237
|
+
const a2 = (cumul + share) * 2 * Math.PI
|
|
238
|
+
|
|
239
|
+
//draw
|
|
240
|
+
ctx.beginPath()
|
|
241
|
+
ctx.moveTo(xc, yc)
|
|
242
|
+
ctx.arc(xc, yc, sG * 0.5, a1 + offAng, a2 + offAng)
|
|
243
|
+
if (this.pieChartInternalRadiusFactor)
|
|
244
|
+
ctx.arc(
|
|
245
|
+
xc,
|
|
246
|
+
yc,
|
|
247
|
+
sG * 0.5 * this.pieChartInternalRadiusFactor,
|
|
248
|
+
a1 + offAng,
|
|
249
|
+
a2 + offAng,
|
|
250
|
+
true
|
|
251
|
+
)
|
|
252
|
+
ctx.closePath()
|
|
253
|
+
ctx.fill()
|
|
254
|
+
} else if (type_ === 'ring') {
|
|
255
|
+
//draw ring
|
|
256
|
+
ctx.beginPath()
|
|
257
|
+
ctx.arc(xc, yc, Math.sqrt(1 - cumul) * sG * 0.5, 0, 2 * Math.PI)
|
|
258
|
+
ctx.fill()
|
|
259
|
+
} else if (type_ === 'segment') {
|
|
260
|
+
//draw segment sections
|
|
261
|
+
const wG = (sG * sG) / resolution
|
|
262
|
+
if (ori == 0) {
|
|
263
|
+
//horizontal
|
|
264
|
+
ctx.fillRect(
|
|
265
|
+
cell.x + offset.dx,
|
|
266
|
+
cell.y + (resolution - wG) / 2 + cumul * wG + offset.dy,
|
|
267
|
+
resolution,
|
|
268
|
+
share * wG
|
|
269
|
+
)
|
|
270
|
+
} else {
|
|
271
|
+
//vertical
|
|
272
|
+
ctx.fillRect(
|
|
273
|
+
cell.x + cumul * resolution + offset.dx,
|
|
274
|
+
cell.y + (resolution - wG) / 2 + offset.dy,
|
|
275
|
+
share * resolution,
|
|
276
|
+
wG
|
|
277
|
+
)
|
|
278
|
+
}
|
|
279
|
+
} else {
|
|
280
|
+
throw new Error('Unexpected symbol type:' + type_)
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
cumul += share
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
//update legends
|
|
289
|
+
this.updateLegends({ style: this, resolution: resolution, z: z, viewScale: viewScale })
|
|
290
|
+
}
|
|
291
|
+
}
|
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
//@ts-check
|
|
2
|
-
'use strict'
|
|
3
|
-
|
|
4
|
-
import { SideStyle } from './SideStyle.js'
|
|
5
|
-
|
|
6
|
-
/** @typedef {{x:number,y:number,or:"v"|"h",value:number}} Side */
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
*
|
|
10
|
-
* @module style
|
|
11
|
-
* @author Julien Gaffuri
|
|
12
|
-
*/
|
|
13
|
-
export class ContourStyle extends SideStyle {
|
|
14
|
-
/** @param {object} opts */
|
|
15
|
-
constructor(opts) {
|
|
16
|
-
super(opts)
|
|
17
|
-
//opts = opts || {}
|
|
18
|
-
|
|
19
|
-
//TODO
|
|
20
|
-
}
|
|
21
|
-
}
|
|
1
|
+
//@ts-check
|
|
2
|
+
'use strict'
|
|
3
|
+
|
|
4
|
+
import { SideStyle } from './SideStyle.js'
|
|
5
|
+
|
|
6
|
+
/** @typedef {{x:number,y:number,or:"v"|"h",value:number}} Side */
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
*
|
|
10
|
+
* @module style
|
|
11
|
+
* @author Julien Gaffuri
|
|
12
|
+
*/
|
|
13
|
+
export class ContourStyle extends SideStyle {
|
|
14
|
+
/** @param {object} opts */
|
|
15
|
+
constructor(opts) {
|
|
16
|
+
super(opts)
|
|
17
|
+
//opts = opts || {}
|
|
18
|
+
|
|
19
|
+
//TODO
|
|
20
|
+
}
|
|
21
|
+
}
|