gridviz 2.1.8 → 2.1.9
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 +125 -125
- package/dist/gridviz.js +8021 -8013
- package/dist/gridviz.min.js +1 -1
- package/package.json +77 -77
- package/src/App.js +705 -705
- package/src/BackgroundLayer.js +182 -182
- package/src/BackgroundLayerWMS.js +131 -131
- package/src/Dataset.js +76 -76
- package/src/DatasetComponent.js +101 -101
- package/src/GeoCanvas.js +420 -420
- package/src/LabelLayer.js +190 -190
- package/src/Layer.js +103 -103
- package/src/Legend.js +86 -86
- package/src/LineLayer.js +150 -150
- package/src/Style.js +129 -129
- package/src/Tooltip.js +180 -172
- package/src/dataset/COGeo.js +24 -24
- package/src/dataset/CSVGrid.js +104 -104
- package/src/dataset/GeoTIFF.js +127 -127
- package/src/dataset/GridTile.js +41 -41
- package/src/dataset/LGrid.js +54 -54
- package/src/dataset/TiledGrid.js +264 -264
- package/src/index.js +71 -71
- package/src/legend/ColorCategoryLegend.js +124 -124
- package/src/legend/ColorDiscreteLegend.js +103 -103
- package/src/legend/ColorLegend.js +124 -124
- package/src/legend/SegmentOrientationLegend.js +87 -87
- package/src/legend/SegmentWidthLegend.js +97 -97
- package/src/legend/SizeLegend.js +141 -141
- package/src/style/CompositionStyle.js +300 -300
- package/src/style/ContourStyle.js +54 -54
- package/src/style/DotDensityStyle.js +156 -156
- package/src/style/IsoFenceStyle.js +262 -262
- package/src/style/JoyPlotStyle.js +135 -135
- package/src/style/LegoStyle.js +112 -112
- package/src/style/MosaicStyle.js +107 -107
- package/src/style/NinjaStarStyle.js +125 -125
- package/src/style/PillarStyle.js +230 -230
- package/src/style/SegmentStyle.js +142 -142
- package/src/style/ShapeColorSizeStyle.js +126 -126
- package/src/style/SideCatStyle.js +172 -172
- package/src/style/SideStyle.js +216 -216
- package/src/style/SquareColorCatWGLStyle.js +120 -120
- package/src/style/SquareColorWGLStyle.js +146 -146
- package/src/style/StrokeStyle.js +131 -131
- package/src/style/TanakaStyle.js +160 -160
- package/src/style/TextStyle.js +132 -132
- package/src/style/TimeSeriesStyle.js +250 -250
- package/src/utils/Utils.js +35 -35
- 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/stretching.js +131 -131
- package/src/utils/webGLUtils.js +71 -71
- package/src/utils/zoomUtils.js +142 -142
|
@@ -1,142 +1,142 @@
|
|
|
1
|
-
//@ts-check
|
|
2
|
-
'use strict'
|
|
3
|
-
|
|
4
|
-
import { Style } from '../Style.js'
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* A style where each cell is represented by a segment whose length, width, color and orientation can vary according to statistical values.
|
|
8
|
-
*
|
|
9
|
-
* @author Julien Gaffuri
|
|
10
|
-
*/
|
|
11
|
-
export class SegmentStyle extends Style {
|
|
12
|
-
/** @param {object} opts */
|
|
13
|
-
constructor(opts) {
|
|
14
|
-
super(opts)
|
|
15
|
-
opts = opts || {}
|
|
16
|
-
|
|
17
|
-
/** A function returning the orientation (in degrees) of the segment representing a cell.
|
|
18
|
-
* @type {function(import("../Dataset").Cell):number} */
|
|
19
|
-
this.orientation = opts.orientation || (() => 0)
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* @type {string} */
|
|
23
|
-
this.colorCol = opts.colorCol
|
|
24
|
-
|
|
25
|
-
/** A function returning the color of the cell segment.
|
|
26
|
-
* @type {function(number,number,import("../Style").Stat|undefined):string} */
|
|
27
|
-
this.color = opts.color || (() => '#EA6BAC')
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* @type {string} */
|
|
31
|
-
this.lengthCol = opts.lengthCol
|
|
32
|
-
|
|
33
|
-
/** A function returning the length of the segment representing a cell, in geo unit
|
|
34
|
-
* @type {function(number,number,import("../Style").Stat|undefined,number):number} */
|
|
35
|
-
this.length = opts.length
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* @type {string} */
|
|
39
|
-
this.widthCol = opts.widthCol
|
|
40
|
-
|
|
41
|
-
/** A function returning the width of the segment representing a cell, in geo unit
|
|
42
|
-
* @type {function(number,number,import("../Style").Stat|undefined,number):number} */
|
|
43
|
-
this.width = opts.width
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* Draw cells as segments.
|
|
48
|
-
*
|
|
49
|
-
* @param {Array.<import("../Dataset").Cell>} cells
|
|
50
|
-
* @param {number} r
|
|
51
|
-
* @param {import("../GeoCanvas").GeoCanvas} cg
|
|
52
|
-
*/
|
|
53
|
-
draw(cells, r, cg) {
|
|
54
|
-
//filter
|
|
55
|
-
if (this.filter) cells = cells.filter(this.filter)
|
|
56
|
-
|
|
57
|
-
//zoom factor
|
|
58
|
-
const zf = cg.getZf()
|
|
59
|
-
|
|
60
|
-
let statColor
|
|
61
|
-
if (this.colorCol) {
|
|
62
|
-
//compute color variable statistics
|
|
63
|
-
statColor = Style.getStatistics(cells, (c) => c[this.colorCol], true)
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
let statLength
|
|
67
|
-
if (this.lengthCol) {
|
|
68
|
-
//if length is used, sort cells by length so that the longests are drawn first
|
|
69
|
-
cells.sort((c1, c2) => c2[this.lengthCol] - c1[this.lengthCol])
|
|
70
|
-
//and compute size variable statistics
|
|
71
|
-
statLength = Style.getStatistics(cells, (c) => c[this.lengthCol], true)
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
let statWidth
|
|
75
|
-
if (this.widthCol) {
|
|
76
|
-
//and compute size variable statistics
|
|
77
|
-
statWidth = Style.getStatistics(cells, (c) => c[this.widthCol], true)
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
//
|
|
81
|
-
cg.ctx.lineCap = 'butt'
|
|
82
|
-
|
|
83
|
-
//conversion factor degree -> radian
|
|
84
|
-
const f = Math.PI / 180
|
|
85
|
-
|
|
86
|
-
//draw in geo coordinates
|
|
87
|
-
cg.setCanvasTransform()
|
|
88
|
-
|
|
89
|
-
for (let c of cells) {
|
|
90
|
-
//color
|
|
91
|
-
/** @type {string|undefined} */
|
|
92
|
-
const col = this.color ? this.color(c[this.colorCol], r, statColor) : undefined
|
|
93
|
-
if (!col) continue
|
|
94
|
-
|
|
95
|
-
//width
|
|
96
|
-
/** @type {number|undefined} */
|
|
97
|
-
const wG = this.width ? this.width(c[this.widthCol], r, statWidth, zf) : undefined
|
|
98
|
-
if (!wG || wG < 0) continue
|
|
99
|
-
|
|
100
|
-
//length
|
|
101
|
-
/** @type {number|undefined} */
|
|
102
|
-
const lG = this.length ? this.length(c[this.lengthCol], r, statLength, zf) : undefined
|
|
103
|
-
if (!lG || lG < 0) continue
|
|
104
|
-
|
|
105
|
-
//orientation (in radian)
|
|
106
|
-
/** @type {number} */
|
|
107
|
-
const or = this.orientation(c) * f
|
|
108
|
-
if (or === undefined || isNaN(or)) continue
|
|
109
|
-
|
|
110
|
-
//get offset
|
|
111
|
-
const offset = this.offset(c, r, zf)
|
|
112
|
-
|
|
113
|
-
//set color and width
|
|
114
|
-
cg.ctx.strokeStyle = col
|
|
115
|
-
cg.ctx.lineWidth = wG
|
|
116
|
-
|
|
117
|
-
//compute segment centre postition
|
|
118
|
-
const cx = c.x + r / 2 + offset.dx
|
|
119
|
-
const cy = c.y + r / 2 + offset.dy
|
|
120
|
-
|
|
121
|
-
//compute segment direction
|
|
122
|
-
const dx = 0.5 * Math.cos(or) * lG
|
|
123
|
-
const dy = 0.5 * Math.sin(or) * lG
|
|
124
|
-
|
|
125
|
-
//draw segment
|
|
126
|
-
cg.ctx.beginPath()
|
|
127
|
-
cg.ctx.moveTo(cx - dx, cy - dy)
|
|
128
|
-
cg.ctx.lineTo(cx + dx, cy + dy)
|
|
129
|
-
cg.ctx.stroke()
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
//update legend, if any
|
|
133
|
-
this.updateLegends({
|
|
134
|
-
style: this,
|
|
135
|
-
r: r,
|
|
136
|
-
zf: zf,
|
|
137
|
-
sColor: statColor,
|
|
138
|
-
sLength: statLength,
|
|
139
|
-
sWidth: statWidth,
|
|
140
|
-
})
|
|
141
|
-
}
|
|
142
|
-
}
|
|
1
|
+
//@ts-check
|
|
2
|
+
'use strict'
|
|
3
|
+
|
|
4
|
+
import { Style } from '../Style.js'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* A style where each cell is represented by a segment whose length, width, color and orientation can vary according to statistical values.
|
|
8
|
+
*
|
|
9
|
+
* @author Julien Gaffuri
|
|
10
|
+
*/
|
|
11
|
+
export class SegmentStyle extends Style {
|
|
12
|
+
/** @param {object} opts */
|
|
13
|
+
constructor(opts) {
|
|
14
|
+
super(opts)
|
|
15
|
+
opts = opts || {}
|
|
16
|
+
|
|
17
|
+
/** A function returning the orientation (in degrees) of the segment representing a cell.
|
|
18
|
+
* @type {function(import("../Dataset").Cell):number} */
|
|
19
|
+
this.orientation = opts.orientation || (() => 0)
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* @type {string} */
|
|
23
|
+
this.colorCol = opts.colorCol
|
|
24
|
+
|
|
25
|
+
/** A function returning the color of the cell segment.
|
|
26
|
+
* @type {function(number,number,import("../Style").Stat|undefined):string} */
|
|
27
|
+
this.color = opts.color || (() => '#EA6BAC')
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* @type {string} */
|
|
31
|
+
this.lengthCol = opts.lengthCol
|
|
32
|
+
|
|
33
|
+
/** A function returning the length of the segment representing a cell, in geo unit
|
|
34
|
+
* @type {function(number,number,import("../Style").Stat|undefined,number):number} */
|
|
35
|
+
this.length = opts.length
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* @type {string} */
|
|
39
|
+
this.widthCol = opts.widthCol
|
|
40
|
+
|
|
41
|
+
/** A function returning the width of the segment representing a cell, in geo unit
|
|
42
|
+
* @type {function(number,number,import("../Style").Stat|undefined,number):number} */
|
|
43
|
+
this.width = opts.width
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Draw cells as segments.
|
|
48
|
+
*
|
|
49
|
+
* @param {Array.<import("../Dataset").Cell>} cells
|
|
50
|
+
* @param {number} r
|
|
51
|
+
* @param {import("../GeoCanvas").GeoCanvas} cg
|
|
52
|
+
*/
|
|
53
|
+
draw(cells, r, cg) {
|
|
54
|
+
//filter
|
|
55
|
+
if (this.filter) cells = cells.filter(this.filter)
|
|
56
|
+
|
|
57
|
+
//zoom factor
|
|
58
|
+
const zf = cg.getZf()
|
|
59
|
+
|
|
60
|
+
let statColor
|
|
61
|
+
if (this.colorCol) {
|
|
62
|
+
//compute color variable statistics
|
|
63
|
+
statColor = Style.getStatistics(cells, (c) => c[this.colorCol], true)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
let statLength
|
|
67
|
+
if (this.lengthCol) {
|
|
68
|
+
//if length is used, sort cells by length so that the longests are drawn first
|
|
69
|
+
cells.sort((c1, c2) => c2[this.lengthCol] - c1[this.lengthCol])
|
|
70
|
+
//and compute size variable statistics
|
|
71
|
+
statLength = Style.getStatistics(cells, (c) => c[this.lengthCol], true)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
let statWidth
|
|
75
|
+
if (this.widthCol) {
|
|
76
|
+
//and compute size variable statistics
|
|
77
|
+
statWidth = Style.getStatistics(cells, (c) => c[this.widthCol], true)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
//
|
|
81
|
+
cg.ctx.lineCap = 'butt'
|
|
82
|
+
|
|
83
|
+
//conversion factor degree -> radian
|
|
84
|
+
const f = Math.PI / 180
|
|
85
|
+
|
|
86
|
+
//draw in geo coordinates
|
|
87
|
+
cg.setCanvasTransform()
|
|
88
|
+
|
|
89
|
+
for (let c of cells) {
|
|
90
|
+
//color
|
|
91
|
+
/** @type {string|undefined} */
|
|
92
|
+
const col = this.color ? this.color(c[this.colorCol], r, statColor) : undefined
|
|
93
|
+
if (!col) continue
|
|
94
|
+
|
|
95
|
+
//width
|
|
96
|
+
/** @type {number|undefined} */
|
|
97
|
+
const wG = this.width ? this.width(c[this.widthCol], r, statWidth, zf) : undefined
|
|
98
|
+
if (!wG || wG < 0) continue
|
|
99
|
+
|
|
100
|
+
//length
|
|
101
|
+
/** @type {number|undefined} */
|
|
102
|
+
const lG = this.length ? this.length(c[this.lengthCol], r, statLength, zf) : undefined
|
|
103
|
+
if (!lG || lG < 0) continue
|
|
104
|
+
|
|
105
|
+
//orientation (in radian)
|
|
106
|
+
/** @type {number} */
|
|
107
|
+
const or = this.orientation(c) * f
|
|
108
|
+
if (or === undefined || isNaN(or)) continue
|
|
109
|
+
|
|
110
|
+
//get offset
|
|
111
|
+
const offset = this.offset(c, r, zf)
|
|
112
|
+
|
|
113
|
+
//set color and width
|
|
114
|
+
cg.ctx.strokeStyle = col
|
|
115
|
+
cg.ctx.lineWidth = wG
|
|
116
|
+
|
|
117
|
+
//compute segment centre postition
|
|
118
|
+
const cx = c.x + r / 2 + offset.dx
|
|
119
|
+
const cy = c.y + r / 2 + offset.dy
|
|
120
|
+
|
|
121
|
+
//compute segment direction
|
|
122
|
+
const dx = 0.5 * Math.cos(or) * lG
|
|
123
|
+
const dy = 0.5 * Math.sin(or) * lG
|
|
124
|
+
|
|
125
|
+
//draw segment
|
|
126
|
+
cg.ctx.beginPath()
|
|
127
|
+
cg.ctx.moveTo(cx - dx, cy - dy)
|
|
128
|
+
cg.ctx.lineTo(cx + dx, cy + dy)
|
|
129
|
+
cg.ctx.stroke()
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
//update legend, if any
|
|
133
|
+
this.updateLegends({
|
|
134
|
+
style: this,
|
|
135
|
+
r: r,
|
|
136
|
+
zf: zf,
|
|
137
|
+
sColor: statColor,
|
|
138
|
+
sLength: statLength,
|
|
139
|
+
sWidth: statWidth,
|
|
140
|
+
})
|
|
141
|
+
}
|
|
142
|
+
}
|
|
@@ -1,126 +1,126 @@
|
|
|
1
|
-
//@ts-check
|
|
2
|
-
'use strict'
|
|
3
|
-
|
|
4
|
-
import { Style } from '../Style.js'
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* A very generic style that shows grid cells with specific color, size and shape.
|
|
8
|
-
* It can be used to show variables as cell colors, cell size, cell shape, or any combination of the three visual variables.
|
|
9
|
-
*
|
|
10
|
-
* @author Joseph Davies, Julien Gaffuri
|
|
11
|
-
*/
|
|
12
|
-
export class ShapeColorSizeStyle extends Style {
|
|
13
|
-
/** @param {object} opts */
|
|
14
|
-
constructor(opts) {
|
|
15
|
-
super(opts)
|
|
16
|
-
opts = opts || {}
|
|
17
|
-
|
|
18
|
-
/** The name of the column/attribute of the tabular data where to retrieve the variable for color.
|
|
19
|
-
* @type {string} */
|
|
20
|
-
this.colorCol = opts.colorCol
|
|
21
|
-
|
|
22
|
-
/** A function returning the color of the cell.
|
|
23
|
-
* @type {function(number,number,import("../Style").Stat|undefined,number):string} */
|
|
24
|
-
this.color = opts.color || (() => '#EA6BAC') //(v,r,s,zf) => {}
|
|
25
|
-
|
|
26
|
-
/** The name of the column/attribute of the tabular data where to retrieve the variable for size.
|
|
27
|
-
* @type {string} */
|
|
28
|
-
this.sizeCol = opts.sizeCol
|
|
29
|
-
|
|
30
|
-
/** A function returning the size of a cell in geographical unit.
|
|
31
|
-
* @type {function(number,number,import("../Style").Stat|undefined,number):number} */
|
|
32
|
-
this.size = opts.size
|
|
33
|
-
|
|
34
|
-
/** A function returning the shape of a cell.
|
|
35
|
-
* @type {function(import("../Dataset").Cell):import("../Style").Shape} */
|
|
36
|
-
this.shape = opts.shape || (() => 'square')
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
* Draw cells as squares, with various colors and size.
|
|
41
|
-
*
|
|
42
|
-
* @param {Array.<import("../Dataset").Cell>} cells
|
|
43
|
-
* @param {number} r
|
|
44
|
-
* @param {import("../GeoCanvas").GeoCanvas} cg
|
|
45
|
-
*/
|
|
46
|
-
draw(cells, r, cg) {
|
|
47
|
-
//filter
|
|
48
|
-
if (this.filter) cells = cells.filter(this.filter)
|
|
49
|
-
|
|
50
|
-
//zoom factor
|
|
51
|
-
const zf = cg.getZf()
|
|
52
|
-
|
|
53
|
-
let statSize
|
|
54
|
-
if (this.sizeCol) {
|
|
55
|
-
//if size is used, sort cells by size so that the biggest are drawn first
|
|
56
|
-
cells.sort((c1, c2) => c2[this.sizeCol] - c1[this.sizeCol])
|
|
57
|
-
//and compute size variable statistics
|
|
58
|
-
statSize = Style.getStatistics(cells, (c) => c[this.sizeCol], true)
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
let statColor
|
|
62
|
-
if (this.colorCol) {
|
|
63
|
-
//compute color variable statistics
|
|
64
|
-
statColor = Style.getStatistics(cells, (c) => c[this.colorCol], true)
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
//draw with HTML canvas
|
|
68
|
-
//in geo coordinates
|
|
69
|
-
cg.setCanvasTransform()
|
|
70
|
-
|
|
71
|
-
const r2 = r * 0.5
|
|
72
|
-
for (let cell of cells) {
|
|
73
|
-
//color
|
|
74
|
-
const col = this.color ? this.color(cell[this.colorCol], r, statColor, zf) : undefined
|
|
75
|
-
if (!col || col === 'none') continue
|
|
76
|
-
cg.ctx.fillStyle = col
|
|
77
|
-
|
|
78
|
-
//shape
|
|
79
|
-
const shape = this.shape ? this.shape(cell) : 'square'
|
|
80
|
-
if (shape === 'none') continue
|
|
81
|
-
|
|
82
|
-
//size
|
|
83
|
-
/** @type {function(number,number,import("../Style").Stat|undefined,number):number} */
|
|
84
|
-
let s_ = this.size || (() => r)
|
|
85
|
-
//size - in geo unit
|
|
86
|
-
const sG = s_(cell[this.sizeCol], r, statSize, zf)
|
|
87
|
-
|
|
88
|
-
//get offset
|
|
89
|
-
const offset = this.offset(cell, r, zf)
|
|
90
|
-
|
|
91
|
-
if (shape === 'square') {
|
|
92
|
-
//draw square
|
|
93
|
-
const d = r * (1 - sG / r) * 0.5
|
|
94
|
-
cg.ctx.fillRect(cell.x + d + offset.dx, cell.y + d + offset.dy, sG, sG)
|
|
95
|
-
} else if (shape === 'circle') {
|
|
96
|
-
//draw circle
|
|
97
|
-
cg.ctx.beginPath()
|
|
98
|
-
cg.ctx.arc(cell.x + r2 + offset.dx, cell.y + r2 + offset.dy, sG * 0.5, 0, 2 * Math.PI, false)
|
|
99
|
-
cg.ctx.fill()
|
|
100
|
-
} else if (shape === 'donut') {
|
|
101
|
-
//draw donut
|
|
102
|
-
const xc = cell.x + r2 + offset.dx,
|
|
103
|
-
yc = cell.y + r2 + offset.dy
|
|
104
|
-
cg.ctx.beginPath()
|
|
105
|
-
cg.ctx.moveTo(xc, yc)
|
|
106
|
-
cg.ctx.arc(xc, yc, r2, 0, 2 * Math.PI)
|
|
107
|
-
cg.ctx.arc(xc, yc, (1 - sG / r) * r2, 0, 2 * Math.PI, true)
|
|
108
|
-
cg.ctx.closePath()
|
|
109
|
-
cg.ctx.fill()
|
|
110
|
-
} else if (shape === 'diamond') {
|
|
111
|
-
const s2 = sG * 0.5
|
|
112
|
-
cg.ctx.beginPath()
|
|
113
|
-
cg.ctx.moveTo(cell.x + r2 - s2, cell.y + r2)
|
|
114
|
-
cg.ctx.lineTo(cell.x + r2, cell.y + r2 + s2)
|
|
115
|
-
cg.ctx.lineTo(cell.x + r2 + s2, cell.y + r2)
|
|
116
|
-
cg.ctx.lineTo(cell.x + r2, cell.y + r2 - s2)
|
|
117
|
-
cg.ctx.fill()
|
|
118
|
-
} else {
|
|
119
|
-
throw new Error('Unexpected shape:' + shape)
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
//update legends
|
|
124
|
-
this.updateLegends({ style: this, r: r, zf: zf, sSize: statSize, sColor: statColor })
|
|
125
|
-
}
|
|
126
|
-
}
|
|
1
|
+
//@ts-check
|
|
2
|
+
'use strict'
|
|
3
|
+
|
|
4
|
+
import { Style } from '../Style.js'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* A very generic style that shows grid cells with specific color, size and shape.
|
|
8
|
+
* It can be used to show variables as cell colors, cell size, cell shape, or any combination of the three visual variables.
|
|
9
|
+
*
|
|
10
|
+
* @author Joseph Davies, Julien Gaffuri
|
|
11
|
+
*/
|
|
12
|
+
export class ShapeColorSizeStyle extends Style {
|
|
13
|
+
/** @param {object} opts */
|
|
14
|
+
constructor(opts) {
|
|
15
|
+
super(opts)
|
|
16
|
+
opts = opts || {}
|
|
17
|
+
|
|
18
|
+
/** The name of the column/attribute of the tabular data where to retrieve the variable for color.
|
|
19
|
+
* @type {string} */
|
|
20
|
+
this.colorCol = opts.colorCol
|
|
21
|
+
|
|
22
|
+
/** A function returning the color of the cell.
|
|
23
|
+
* @type {function(number,number,import("../Style").Stat|undefined,number):string} */
|
|
24
|
+
this.color = opts.color || (() => '#EA6BAC') //(v,r,s,zf) => {}
|
|
25
|
+
|
|
26
|
+
/** The name of the column/attribute of the tabular data where to retrieve the variable for size.
|
|
27
|
+
* @type {string} */
|
|
28
|
+
this.sizeCol = opts.sizeCol
|
|
29
|
+
|
|
30
|
+
/** A function returning the size of a cell in geographical unit.
|
|
31
|
+
* @type {function(number,number,import("../Style").Stat|undefined,number):number} */
|
|
32
|
+
this.size = opts.size
|
|
33
|
+
|
|
34
|
+
/** A function returning the shape of a cell.
|
|
35
|
+
* @type {function(import("../Dataset").Cell):import("../Style").Shape} */
|
|
36
|
+
this.shape = opts.shape || (() => 'square')
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Draw cells as squares, with various colors and size.
|
|
41
|
+
*
|
|
42
|
+
* @param {Array.<import("../Dataset").Cell>} cells
|
|
43
|
+
* @param {number} r
|
|
44
|
+
* @param {import("../GeoCanvas").GeoCanvas} cg
|
|
45
|
+
*/
|
|
46
|
+
draw(cells, r, cg) {
|
|
47
|
+
//filter
|
|
48
|
+
if (this.filter) cells = cells.filter(this.filter)
|
|
49
|
+
|
|
50
|
+
//zoom factor
|
|
51
|
+
const zf = cg.getZf()
|
|
52
|
+
|
|
53
|
+
let statSize
|
|
54
|
+
if (this.sizeCol) {
|
|
55
|
+
//if size is used, sort cells by size so that the biggest are drawn first
|
|
56
|
+
cells.sort((c1, c2) => c2[this.sizeCol] - c1[this.sizeCol])
|
|
57
|
+
//and compute size variable statistics
|
|
58
|
+
statSize = Style.getStatistics(cells, (c) => c[this.sizeCol], true)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
let statColor
|
|
62
|
+
if (this.colorCol) {
|
|
63
|
+
//compute color variable statistics
|
|
64
|
+
statColor = Style.getStatistics(cells, (c) => c[this.colorCol], true)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
//draw with HTML canvas
|
|
68
|
+
//in geo coordinates
|
|
69
|
+
cg.setCanvasTransform()
|
|
70
|
+
|
|
71
|
+
const r2 = r * 0.5
|
|
72
|
+
for (let cell of cells) {
|
|
73
|
+
//color
|
|
74
|
+
const col = this.color ? this.color(cell[this.colorCol], r, statColor, zf) : undefined
|
|
75
|
+
if (!col || col === 'none') continue
|
|
76
|
+
cg.ctx.fillStyle = col
|
|
77
|
+
|
|
78
|
+
//shape
|
|
79
|
+
const shape = this.shape ? this.shape(cell) : 'square'
|
|
80
|
+
if (shape === 'none') continue
|
|
81
|
+
|
|
82
|
+
//size
|
|
83
|
+
/** @type {function(number,number,import("../Style").Stat|undefined,number):number} */
|
|
84
|
+
let s_ = this.size || (() => r)
|
|
85
|
+
//size - in geo unit
|
|
86
|
+
const sG = s_(cell[this.sizeCol], r, statSize, zf)
|
|
87
|
+
|
|
88
|
+
//get offset
|
|
89
|
+
const offset = this.offset(cell, r, zf)
|
|
90
|
+
|
|
91
|
+
if (shape === 'square') {
|
|
92
|
+
//draw square
|
|
93
|
+
const d = r * (1 - sG / r) * 0.5
|
|
94
|
+
cg.ctx.fillRect(cell.x + d + offset.dx, cell.y + d + offset.dy, sG, sG)
|
|
95
|
+
} else if (shape === 'circle') {
|
|
96
|
+
//draw circle
|
|
97
|
+
cg.ctx.beginPath()
|
|
98
|
+
cg.ctx.arc(cell.x + r2 + offset.dx, cell.y + r2 + offset.dy, sG * 0.5, 0, 2 * Math.PI, false)
|
|
99
|
+
cg.ctx.fill()
|
|
100
|
+
} else if (shape === 'donut') {
|
|
101
|
+
//draw donut
|
|
102
|
+
const xc = cell.x + r2 + offset.dx,
|
|
103
|
+
yc = cell.y + r2 + offset.dy
|
|
104
|
+
cg.ctx.beginPath()
|
|
105
|
+
cg.ctx.moveTo(xc, yc)
|
|
106
|
+
cg.ctx.arc(xc, yc, r2, 0, 2 * Math.PI)
|
|
107
|
+
cg.ctx.arc(xc, yc, (1 - sG / r) * r2, 0, 2 * Math.PI, true)
|
|
108
|
+
cg.ctx.closePath()
|
|
109
|
+
cg.ctx.fill()
|
|
110
|
+
} else if (shape === 'diamond') {
|
|
111
|
+
const s2 = sG * 0.5
|
|
112
|
+
cg.ctx.beginPath()
|
|
113
|
+
cg.ctx.moveTo(cell.x + r2 - s2, cell.y + r2)
|
|
114
|
+
cg.ctx.lineTo(cell.x + r2, cell.y + r2 + s2)
|
|
115
|
+
cg.ctx.lineTo(cell.x + r2 + s2, cell.y + r2)
|
|
116
|
+
cg.ctx.lineTo(cell.x + r2, cell.y + r2 - s2)
|
|
117
|
+
cg.ctx.fill()
|
|
118
|
+
} else {
|
|
119
|
+
throw new Error('Unexpected shape:' + shape)
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
//update legends
|
|
124
|
+
this.updateLegends({ style: this, r: r, zf: zf, sSize: statSize, sColor: statColor })
|
|
125
|
+
}
|
|
126
|
+
}
|