gridviz 3.1.0 → 3.1.1
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 +18 -15
- package/dist/gridviz.js +3829 -2623
- package/dist/gridviz.min.js +1 -1
- package/package.json +5 -3
- package/src/button/Button.js +77 -68
- package/src/button/FullscreenButton.js +123 -123
- package/src/button/ZoomButtons.js +109 -104
- package/src/core/GeoCanvas.js +15 -0
- package/src/core/Legend.js +267 -245
- package/src/core/Map.js +819 -670
- package/src/core/Tooltip.js +191 -185
- package/src/index.js +82 -79
- package/src/layer/GridLayer.js +4 -3
- package/src/layer/LabelLayer.js +7 -4
- package/src/legend/ColorCategoryLegend.js +127 -125
- package/src/legend/ColorDiscreteLegend.js +116 -114
- package/src/legend/ColorLegend.js +33 -11
- package/src/legend/OrientationLegend.js +94 -92
- package/src/legend/SizeLegend.js +8 -1
- package/src/legend/TernaryLegend.js +317 -314
- package/src/legend/VennLegend.js +202 -0
- package/src/style/CircleColorSizeWebGLStyle.js +249 -0
- package/src/style/CompositionStyle.js +1 -1
- package/src/style/DotDensityStyle.js +8 -4
- package/src/style/ImageStyle.js +1 -1
- package/src/style/Interpolator.js +283 -259
- package/src/style/IsoFenceStyle.js +1 -1
- package/src/style/LegoStyle.js +6 -5
- package/src/style/MosaicStyle.js +1 -1
- package/src/style/NinjaStarStyle.js +1 -1
- package/src/style/PillarStyle.js +1 -1
- package/src/style/RandomWalkInterpolator.js +213 -0
- package/src/style/ShadingRayStyle.js +16 -2
- package/src/style/ShadingStyle.js +80 -72
- package/src/style/ShapeColorSizeStyle.js +18 -2
- package/src/style/SideCategoryStyle.js +1 -1
- package/src/style/SideStyle.js +1 -1
- package/src/style/SideTanakaStyle.js +1 -1
- package/src/style/SquareColorCategoryWebGLStyle.js +71 -20
- package/src/style/SquareColorWebGLStyle.js +7 -4
- package/src/style/StrokeStyle.js +1 -1
- package/src/style/TextStyle.js +1 -1
- package/src/style/TimeSeriesStyle.js +14 -7
- package/src/utils/WebGLSquareColoringAdvanced.js +47 -9
- package/src/utils/scale.js +20 -0
- package/src/utils/webGLUtils.js +52 -0
|
@@ -1,125 +1,127 @@
|
|
|
1
|
-
//@ts-check
|
|
2
|
-
'use strict'
|
|
3
|
-
|
|
4
|
-
import { Legend } from '../core/Legend.js'
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* A legend element for color categrories.
|
|
8
|
-
*
|
|
9
|
-
* @module legend
|
|
10
|
-
* @author Joseph Davies, Julien Gaffuri
|
|
11
|
-
*/
|
|
12
|
-
export class ColorCategoryLegend extends Legend {
|
|
13
|
-
/** @param {Object} opts */
|
|
14
|
-
constructor(opts) {
|
|
15
|
-
super(opts)
|
|
16
|
-
opts = opts || {}
|
|
17
|
-
|
|
18
|
-
//col/categories array, in display order
|
|
19
|
-
/**
|
|
20
|
-
* @private
|
|
21
|
-
* @type {Array.<[string,string]>} */
|
|
22
|
-
this.colorLabel = opts.colorLabel || [['gray', '-']]
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* @private
|
|
26
|
-
* @type {import("../core/Style.js").Shape} */
|
|
27
|
-
this.shape = opts.shape || 'circle'
|
|
28
|
-
this.dimension = opts.dimension || { r: 8 }
|
|
29
|
-
this.strokeColor = opts.strokeColor || 'gray'
|
|
30
|
-
this.strokeWidth = opts.strokeWidth || 1
|
|
31
|
-
this.setType('category')
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
*/
|
|
36
|
-
update() {
|
|
37
|
-
//clear
|
|
38
|
-
this.div.selectAll('*').remove()
|
|
39
|
-
|
|
40
|
-
//title
|
|
41
|
-
this.makeTitle()
|
|
42
|
-
|
|
43
|
-
//categories
|
|
44
|
-
const nb = this.colorLabel.length
|
|
45
|
-
if (nb == 0) return
|
|
46
|
-
|
|
47
|
-
for (let i = 0; i < nb; i++) {
|
|
48
|
-
const cat = this.colorLabel[i]
|
|
49
|
-
|
|
50
|
-
//make div for category
|
|
51
|
-
const d = this.div.append('div').classed('gridviz-legend-category', true)
|
|
52
|
-
// hovering a category shows only its cells on the map
|
|
53
|
-
this.bindHover(d, cat[0], {
|
|
54
|
-
enter: () => d.classed('gridviz-legend-category--active', true).select('svg').select('rect, circle').style('stroke', 'red'),
|
|
55
|
-
leave: () => d.classed('gridviz-legend-category--active', false).select('svg').select('rect, circle').style('stroke', this.strokeColor),
|
|
56
|
-
})
|
|
57
|
-
//to enable vertical centering
|
|
58
|
-
//.style("position", "relative")
|
|
59
|
-
|
|
60
|
-
const sw = this.strokeWidth
|
|
61
|
-
|
|
62
|
-
//draw graphic element: box / circle
|
|
63
|
-
if (this.shape === 'square') {
|
|
64
|
-
const h = this.dimension.h || 15
|
|
65
|
-
const w = this.dimension.w || 20
|
|
66
|
-
d.append('div')
|
|
67
|
-
.classed('gridviz-legend-symbol-wrapper', true)
|
|
68
|
-
.style('display', 'inline')
|
|
69
|
-
|
|
70
|
-
.append('svg')
|
|
71
|
-
.classed('gridviz-legend-graphic', true)
|
|
72
|
-
.attr('width', w + 2 * sw)
|
|
73
|
-
.attr('height', h + 2 * sw)
|
|
74
|
-
|
|
75
|
-
.append('rect')
|
|
76
|
-
.classed('gridviz-legend-swatch', true)
|
|
77
|
-
.attr('x', sw)
|
|
78
|
-
.attr('y', sw)
|
|
79
|
-
.attr('width', w)
|
|
80
|
-
.attr('height', h)
|
|
81
|
-
.style('fill', cat[0])
|
|
82
|
-
.style('stroke', this.strokeColor)
|
|
83
|
-
.style('stroke-width', this.strokeWidth)
|
|
84
|
-
} else if (this.shape === 'circle') {
|
|
85
|
-
const r = this.dimension.r || 8
|
|
86
|
-
const h = 2 * r + 2 * sw
|
|
87
|
-
d.append('div')
|
|
88
|
-
.classed('gridviz-legend-symbol-wrapper', true)
|
|
89
|
-
.style('display', 'inline')
|
|
90
|
-
|
|
91
|
-
.append('svg')
|
|
92
|
-
.classed('gridviz-legend-graphic', true)
|
|
93
|
-
.attr('width', h)
|
|
94
|
-
.attr('height', h)
|
|
95
|
-
|
|
96
|
-
.append('circle')
|
|
97
|
-
.classed('gridviz-legend-swatch', true)
|
|
98
|
-
.attr('cx', r + sw)
|
|
99
|
-
.attr('cy', r + sw)
|
|
100
|
-
.attr('r', r)
|
|
101
|
-
.style('fill', cat[0])
|
|
102
|
-
.style('stroke', this.strokeColor)
|
|
103
|
-
.style('stroke-width', this.strokeWidth)
|
|
104
|
-
} else {
|
|
105
|
-
throw new Error('Unexpected shape:' + this.shape)
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
//write label text
|
|
109
|
-
this.setText(
|
|
110
|
-
d
|
|
111
|
-
.append('div')
|
|
112
|
-
//show on right of graphic
|
|
113
|
-
.style('display', 'inline')
|
|
114
|
-
|
|
115
|
-
//center vertically
|
|
116
|
-
//.style("position", "absolute").style("top", "0").style("bottom", "0")
|
|
117
|
-
|
|
118
|
-
.classed('gridviz-legend-label', true)
|
|
119
|
-
.style('padding-left', '5px')
|
|
120
|
-
.style('font-size', this.labelFontSizeCSS())
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
}
|
|
1
|
+
//@ts-check
|
|
2
|
+
'use strict'
|
|
3
|
+
|
|
4
|
+
import { Legend } from '../core/Legend.js'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* A legend element for color categrories.
|
|
8
|
+
*
|
|
9
|
+
* @module legend
|
|
10
|
+
* @author Joseph Davies, Julien Gaffuri
|
|
11
|
+
*/
|
|
12
|
+
export class ColorCategoryLegend extends Legend {
|
|
13
|
+
/** @param {Object} opts */
|
|
14
|
+
constructor(opts) {
|
|
15
|
+
super(opts)
|
|
16
|
+
opts = opts || {}
|
|
17
|
+
|
|
18
|
+
//col/categories array, in display order
|
|
19
|
+
/**
|
|
20
|
+
* @private
|
|
21
|
+
* @type {Array.<[string,string]>} */
|
|
22
|
+
this.colorLabel = opts.colorLabel || [['gray', '-']]
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* @private
|
|
26
|
+
* @type {import("../core/Style.js").Shape} */
|
|
27
|
+
this.shape = opts.shape || 'circle'
|
|
28
|
+
this.dimension = opts.dimension || { r: 8 }
|
|
29
|
+
this.strokeColor = opts.strokeColor || 'gray'
|
|
30
|
+
this.strokeWidth = opts.strokeWidth || 1
|
|
31
|
+
this.setType('category')
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
*/
|
|
36
|
+
update() {
|
|
37
|
+
//clear
|
|
38
|
+
this.div.selectAll('*').remove()
|
|
39
|
+
|
|
40
|
+
//title
|
|
41
|
+
this.makeTitle()
|
|
42
|
+
|
|
43
|
+
//categories
|
|
44
|
+
const nb = this.colorLabel.length
|
|
45
|
+
if (nb == 0) return
|
|
46
|
+
|
|
47
|
+
for (let i = 0; i < nb; i++) {
|
|
48
|
+
const cat = this.colorLabel[i]
|
|
49
|
+
|
|
50
|
+
//make div for category
|
|
51
|
+
const d = this.div.append('div').classed('gridviz-legend-category', true)
|
|
52
|
+
// hovering a category shows only its cells on the map
|
|
53
|
+
this.bindHover(d, cat[0], {
|
|
54
|
+
enter: () => d.classed('gridviz-legend-category--active', true).select('svg').select('rect, circle').style('stroke', 'red'),
|
|
55
|
+
leave: () => d.classed('gridviz-legend-category--active', false).select('svg').select('rect, circle').style('stroke', this.strokeColor),
|
|
56
|
+
})
|
|
57
|
+
//to enable vertical centering
|
|
58
|
+
//.style("position", "relative")
|
|
59
|
+
|
|
60
|
+
const sw = this.strokeWidth
|
|
61
|
+
|
|
62
|
+
//draw graphic element: box / circle
|
|
63
|
+
if (this.shape === 'square') {
|
|
64
|
+
const h = this.dimension.h || 15
|
|
65
|
+
const w = this.dimension.w || 20
|
|
66
|
+
d.append('div')
|
|
67
|
+
.classed('gridviz-legend-symbol-wrapper', true)
|
|
68
|
+
.style('display', 'inline')
|
|
69
|
+
|
|
70
|
+
.append('svg')
|
|
71
|
+
.classed('gridviz-legend-graphic', true)
|
|
72
|
+
.attr('width', w + 2 * sw)
|
|
73
|
+
.attr('height', h + 2 * sw)
|
|
74
|
+
|
|
75
|
+
.append('rect')
|
|
76
|
+
.classed('gridviz-legend-swatch', true)
|
|
77
|
+
.attr('x', sw)
|
|
78
|
+
.attr('y', sw)
|
|
79
|
+
.attr('width', w)
|
|
80
|
+
.attr('height', h)
|
|
81
|
+
.style('fill', cat[0])
|
|
82
|
+
.style('stroke', this.strokeColor)
|
|
83
|
+
.style('stroke-width', this.strokeWidth)
|
|
84
|
+
} else if (this.shape === 'circle') {
|
|
85
|
+
const r = this.dimension.r || 8
|
|
86
|
+
const h = 2 * r + 2 * sw
|
|
87
|
+
d.append('div')
|
|
88
|
+
.classed('gridviz-legend-symbol-wrapper', true)
|
|
89
|
+
.style('display', 'inline')
|
|
90
|
+
|
|
91
|
+
.append('svg')
|
|
92
|
+
.classed('gridviz-legend-graphic', true)
|
|
93
|
+
.attr('width', h)
|
|
94
|
+
.attr('height', h)
|
|
95
|
+
|
|
96
|
+
.append('circle')
|
|
97
|
+
.classed('gridviz-legend-swatch', true)
|
|
98
|
+
.attr('cx', r + sw)
|
|
99
|
+
.attr('cy', r + sw)
|
|
100
|
+
.attr('r', r)
|
|
101
|
+
.style('fill', cat[0])
|
|
102
|
+
.style('stroke', this.strokeColor)
|
|
103
|
+
.style('stroke-width', this.strokeWidth)
|
|
104
|
+
} else {
|
|
105
|
+
throw new Error('Unexpected shape:' + this.shape)
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
//write label text
|
|
109
|
+
this.setText(
|
|
110
|
+
d
|
|
111
|
+
.append('div')
|
|
112
|
+
//show on right of graphic
|
|
113
|
+
.style('display', 'inline')
|
|
114
|
+
|
|
115
|
+
//center vertically
|
|
116
|
+
//.style("position", "absolute").style("top", "0").style("bottom", "0")
|
|
117
|
+
|
|
118
|
+
.classed('gridviz-legend-label', true)
|
|
119
|
+
.style('padding-left', '5px')
|
|
120
|
+
.style('font-size', this.labelFontSizeCSS())
|
|
121
|
+
.style('fill', this.labelColorCSS())
|
|
122
|
+
.style('color', this.labelColorCSS()),
|
|
123
|
+
cat[1]
|
|
124
|
+
)
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
@@ -1,114 +1,116 @@
|
|
|
1
|
-
//@ts-check
|
|
2
|
-
'use strict'
|
|
3
|
-
|
|
4
|
-
import { Legend } from '../core/Legend.js'
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* A legend element for discrete color style.
|
|
8
|
-
* Inspiration: https://observablehq.com/@d3/color-legend
|
|
9
|
-
*
|
|
10
|
-
* @module legend
|
|
11
|
-
* @author Julien Gaffuri
|
|
12
|
-
*/
|
|
13
|
-
export class ColorDiscreteLegend extends Legend {
|
|
14
|
-
/** @param {Object} opts */
|
|
15
|
-
constructor(opts) {
|
|
16
|
-
super(opts)
|
|
17
|
-
opts = opts || {}
|
|
18
|
-
|
|
19
|
-
/** @private @type {function(import('../core/Style').ViewScale):Array.<string>} */
|
|
20
|
-
this.colors = opts.colors
|
|
21
|
-
/** @private @type {function(import('../core/Style').ViewScale):Array.<number>} */
|
|
22
|
-
this.breaks = opts.breaks
|
|
23
|
-
|
|
24
|
-
// a number of pixels, or 'fill' for the width of the container
|
|
25
|
-
this.width = opts.width === 'fill' ? 300 : opts.width || 300
|
|
26
|
-
this.height = opts.height || 15
|
|
27
|
-
this.setType('discrete')
|
|
28
|
-
|
|
29
|
-
this.tickSize = opts.tickSize || 3
|
|
30
|
-
|
|
31
|
-
//label
|
|
32
|
-
this.invert = opts.invert
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* @param {{viewScale:import('../core/Style').ViewScale} } opts
|
|
37
|
-
*/
|
|
38
|
-
update(opts) {
|
|
39
|
-
//clear
|
|
40
|
-
this.div.selectAll('*').remove()
|
|
41
|
-
|
|
42
|
-
//title
|
|
43
|
-
this.makeTitle()
|
|
44
|
-
|
|
45
|
-
//get colors and breaks
|
|
46
|
-
const colors = this.colors(opts.viewScale)
|
|
47
|
-
const breaks = this.breaks(opts.viewScale)
|
|
48
|
-
if (!breaks) return
|
|
49
|
-
|
|
50
|
-
//classes
|
|
51
|
-
const nb = colors.length
|
|
52
|
-
if (nb == 0) return
|
|
53
|
-
const width = this.availableWidth(this.width)
|
|
54
|
-
const w = width / nb
|
|
55
|
-
|
|
56
|
-
//make svg element
|
|
57
|
-
const svg = this.div
|
|
58
|
-
.append('svg')
|
|
59
|
-
.classed('gridviz-legend-graphic', true)
|
|
60
|
-
.attr('width', width)
|
|
61
|
-
.attr('height', this.height + this.tickSize + 2 + 10)
|
|
62
|
-
|
|
63
|
-
//draw graphic elements
|
|
64
|
-
for (let i = 0; i < nb; i++) {
|
|
65
|
-
const rect = svg
|
|
66
|
-
.append('rect')
|
|
67
|
-
.classed('gridviz-legend-swatch', true)
|
|
68
|
-
.attr('x', i * w)
|
|
69
|
-
.attr('y', 0)
|
|
70
|
-
.attr('width', w)
|
|
71
|
-
.attr('height', this.height)
|
|
72
|
-
.style('fill', colors[i])
|
|
73
|
-
// hovering a class shows only its cells on the map
|
|
74
|
-
this.bindHover(rect, colors[i], {
|
|
75
|
-
// a presentation attribute, so that a stylesheet can restyle the active swatch
|
|
76
|
-
enter: () => rect.classed('gridviz-legend-swatch--active', true).attr('stroke', 'red'),
|
|
77
|
-
leave: () => rect.classed('gridviz-legend-swatch--active', false).attr('stroke', null),
|
|
78
|
-
})
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
//tick line
|
|
82
|
-
for (let i = 1; i < nb; i++) {
|
|
83
|
-
svg.append('line')
|
|
84
|
-
.classed('gridviz-legend-tick', true)
|
|
85
|
-
.attr('x1', w * i)
|
|
86
|
-
.attr('y1', 0)
|
|
87
|
-
.attr('x2', w * i)
|
|
88
|
-
.attr('y2', this.height + this.tickSize)
|
|
89
|
-
.style('stroke', 'black')
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
//labels
|
|
93
|
-
for (let i = 1; i < nb; i++) {
|
|
94
|
-
let label = breaks[i - 1]
|
|
95
|
-
if (isNaN(label) || label == undefined) continue
|
|
96
|
-
if (this.labelFormat) label = this.labelFormat(label, i)
|
|
97
|
-
|
|
98
|
-
//label
|
|
99
|
-
svg.append('text')
|
|
100
|
-
.classed('gridviz-legend-label', true)
|
|
101
|
-
.attr('id', 'ticklabel_' + i)
|
|
102
|
-
.attr('x', w * i)
|
|
103
|
-
.attr('y', this.height + this.tickSize + 2)
|
|
104
|
-
.style('font-size', this.labelFontSizeCSS())
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
.style('
|
|
110
|
-
.style('
|
|
111
|
-
.
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
}
|
|
1
|
+
//@ts-check
|
|
2
|
+
'use strict'
|
|
3
|
+
|
|
4
|
+
import { Legend } from '../core/Legend.js'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* A legend element for discrete color style.
|
|
8
|
+
* Inspiration: https://observablehq.com/@d3/color-legend
|
|
9
|
+
*
|
|
10
|
+
* @module legend
|
|
11
|
+
* @author Julien Gaffuri
|
|
12
|
+
*/
|
|
13
|
+
export class ColorDiscreteLegend extends Legend {
|
|
14
|
+
/** @param {Object} opts */
|
|
15
|
+
constructor(opts) {
|
|
16
|
+
super(opts)
|
|
17
|
+
opts = opts || {}
|
|
18
|
+
|
|
19
|
+
/** @private @type {function(import('../core/Style').ViewScale):Array.<string>} */
|
|
20
|
+
this.colors = opts.colors
|
|
21
|
+
/** @private @type {function(import('../core/Style').ViewScale):Array.<number>} */
|
|
22
|
+
this.breaks = opts.breaks
|
|
23
|
+
|
|
24
|
+
// a number of pixels, or 'fill' for the width of the container
|
|
25
|
+
this.width = opts.width === 'fill' ? 300 : opts.width || 300
|
|
26
|
+
this.height = opts.height || 15
|
|
27
|
+
this.setType('discrete')
|
|
28
|
+
|
|
29
|
+
this.tickSize = opts.tickSize || 3
|
|
30
|
+
|
|
31
|
+
//label
|
|
32
|
+
this.invert = opts.invert
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* @param {{viewScale:import('../core/Style').ViewScale} } opts
|
|
37
|
+
*/
|
|
38
|
+
update(opts) {
|
|
39
|
+
//clear
|
|
40
|
+
this.div.selectAll('*').remove()
|
|
41
|
+
|
|
42
|
+
//title
|
|
43
|
+
this.makeTitle()
|
|
44
|
+
|
|
45
|
+
//get colors and breaks
|
|
46
|
+
const colors = this.colors(opts.viewScale)
|
|
47
|
+
const breaks = this.breaks(opts.viewScale)
|
|
48
|
+
if (!breaks) return
|
|
49
|
+
|
|
50
|
+
//classes
|
|
51
|
+
const nb = colors.length
|
|
52
|
+
if (nb == 0) return
|
|
53
|
+
const width = this.availableWidth(this.width)
|
|
54
|
+
const w = width / nb
|
|
55
|
+
|
|
56
|
+
//make svg element
|
|
57
|
+
const svg = this.div
|
|
58
|
+
.append('svg')
|
|
59
|
+
.classed('gridviz-legend-graphic', true)
|
|
60
|
+
.attr('width', width)
|
|
61
|
+
.attr('height', this.height + this.tickSize + 2 + 10)
|
|
62
|
+
|
|
63
|
+
//draw graphic elements
|
|
64
|
+
for (let i = 0; i < nb; i++) {
|
|
65
|
+
const rect = svg
|
|
66
|
+
.append('rect')
|
|
67
|
+
.classed('gridviz-legend-swatch', true)
|
|
68
|
+
.attr('x', i * w)
|
|
69
|
+
.attr('y', 0)
|
|
70
|
+
.attr('width', w)
|
|
71
|
+
.attr('height', this.height)
|
|
72
|
+
.style('fill', colors[i])
|
|
73
|
+
// hovering a class shows only its cells on the map
|
|
74
|
+
this.bindHover(rect, colors[i], {
|
|
75
|
+
// a presentation attribute, so that a stylesheet can restyle the active swatch
|
|
76
|
+
enter: () => rect.classed('gridviz-legend-swatch--active', true).attr('stroke', 'red'),
|
|
77
|
+
leave: () => rect.classed('gridviz-legend-swatch--active', false).attr('stroke', null),
|
|
78
|
+
})
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
//tick line
|
|
82
|
+
for (let i = 1; i < nb; i++) {
|
|
83
|
+
svg.append('line')
|
|
84
|
+
.classed('gridviz-legend-tick', true)
|
|
85
|
+
.attr('x1', w * i)
|
|
86
|
+
.attr('y1', 0)
|
|
87
|
+
.attr('x2', w * i)
|
|
88
|
+
.attr('y2', this.height + this.tickSize)
|
|
89
|
+
.style('stroke', 'black')
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
//labels
|
|
93
|
+
for (let i = 1; i < nb; i++) {
|
|
94
|
+
let label = breaks[i - 1]
|
|
95
|
+
if (isNaN(label) || label == undefined) continue
|
|
96
|
+
if (this.labelFormat) label = this.labelFormat(label, i)
|
|
97
|
+
|
|
98
|
+
//label
|
|
99
|
+
svg.append('text')
|
|
100
|
+
.classed('gridviz-legend-label', true)
|
|
101
|
+
.attr('id', 'ticklabel_' + i)
|
|
102
|
+
.attr('x', w * i)
|
|
103
|
+
.attr('y', this.height + this.tickSize + 2)
|
|
104
|
+
.style('font-size', this.labelFontSizeCSS())
|
|
105
|
+
.style('fill', this.labelColorCSS())
|
|
106
|
+
.style('color', this.labelColorCSS())
|
|
107
|
+
//.style("font-weight", "bold")
|
|
108
|
+
//.style("font-family", "Arial")
|
|
109
|
+
.style('text-anchor', 'middle')
|
|
110
|
+
.style('alignment-baseline', 'top')
|
|
111
|
+
.style('dominant-baseline', 'hanging')
|
|
112
|
+
.style('pointer-events', 'none')
|
|
113
|
+
.text(label)
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
@@ -26,21 +26,39 @@ export class ColorLegend extends Legend {
|
|
|
26
26
|
|
|
27
27
|
this.margin = opts.margin || 5
|
|
28
28
|
|
|
29
|
+
// a number of pixels, or 'fill' for the width of the container
|
|
30
|
+
this.width = opts.width === 'fill' ? 300 : opts.width || 300
|
|
31
|
+
this.height = opts.height || 15
|
|
32
|
+
|
|
29
33
|
//replace with labels ?
|
|
30
34
|
this.tickSize = opts.tickSize || 6
|
|
31
|
-
|
|
35
|
+
/** The number of ticks. By default one for each 50 pixels of the width the legend is drawn at, at least 2.
|
|
36
|
+
* @type {number} */
|
|
37
|
+
this.ticks = opts.ticks || Math.max(2, Math.floor(this.width / 50))
|
|
38
|
+
/** The default number of ticks, when no number is given: it follows the width the legend is drawn at (@see update)
|
|
39
|
+
* @private */
|
|
40
|
+
this._defaultTicks = opts.ticks ? undefined : this.ticks
|
|
32
41
|
this.tickFormat = opts.tickFormat
|
|
33
42
|
this.tickUnit = opts.tickUnit
|
|
34
43
|
|
|
35
|
-
|
|
44
|
+
/** The font size of the tick labels. By default, the one of the legend labels (labelFontSize).
|
|
45
|
+
* @type {string|undefined} */
|
|
46
|
+
this.fontSize = opts.fontSize
|
|
36
47
|
this.invert = opts.invert
|
|
37
48
|
|
|
38
|
-
// a number of pixels, or 'fill' for the width of the container
|
|
39
|
-
this.width = opts.width === 'fill' ? 300 : opts.width || 300
|
|
40
|
-
this.height = opts.height || 15
|
|
41
49
|
this.setType('color')
|
|
42
50
|
}
|
|
43
51
|
|
|
52
|
+
/**
|
|
53
|
+
* The number of ticks of the bar, for a width of it: the one given, or one for each 50 pixels (at least two: the two ends).
|
|
54
|
+
* @param {number} width The width the bar is drawn at, in pixels.
|
|
55
|
+
* @returns {number}
|
|
56
|
+
*/
|
|
57
|
+
tickCount(width) {
|
|
58
|
+
if (this._defaultTicks !== undefined && this.ticks === this._defaultTicks) return Math.max(2, Math.floor(width / 50))
|
|
59
|
+
return Math.max(2, this.ticks)
|
|
60
|
+
}
|
|
61
|
+
|
|
44
62
|
/**
|
|
45
63
|
* @param {{viewScale:import('../core/Style').ViewScale} } opts
|
|
46
64
|
*/
|
|
@@ -52,6 +70,9 @@ export class ColorLegend extends Legend {
|
|
|
52
70
|
this.makeTitle()
|
|
53
71
|
|
|
54
72
|
const width = this.availableWidth(this.width)
|
|
73
|
+
// without a given number of ticks: one for each 50 pixels of the width the legend is drawn at (the one of its container
|
|
74
|
+
// with width:'fill'). At least two: the two ends.
|
|
75
|
+
const nbTicks = this.tickCount(width)
|
|
55
76
|
const svgW = width + 2 * this.margin
|
|
56
77
|
const svgH = this.height + this.margin + this.tickSize + 10
|
|
57
78
|
const svg = this.div.append('svg').classed('gridviz-legend-graphic', true).attr('width', svgW).attr('height', svgH)
|
|
@@ -75,8 +96,8 @@ export class ColorLegend extends Legend {
|
|
|
75
96
|
.style('fill', this.colorScale(t, opts.viewScale))
|
|
76
97
|
}
|
|
77
98
|
|
|
78
|
-
for (let i = 0; i <
|
|
79
|
-
let t = i / (
|
|
99
|
+
for (let i = 0; i < nbTicks; i++) {
|
|
100
|
+
let t = i / (nbTicks - 1)
|
|
80
101
|
|
|
81
102
|
//tick line
|
|
82
103
|
g.append('line')
|
|
@@ -93,10 +114,11 @@ export class ColorLegend extends Legend {
|
|
|
93
114
|
.attr('id', 'ticklabel_' + i)
|
|
94
115
|
.attr('x', w * t)
|
|
95
116
|
.attr('y', h + this.tickSize + 2)
|
|
96
|
-
.style('font-size', this.fontSize)
|
|
117
|
+
.style('font-size', this.fontSize || this.labelFontSizeCSS())
|
|
118
|
+
.style('fill', this.labelColorCSS())
|
|
97
119
|
//.style("font-weight", "bold")
|
|
98
120
|
//.style("font-family", "Arial")
|
|
99
|
-
.style('text-anchor', i == 0 ? 'start' : i ==
|
|
121
|
+
.style('text-anchor', i == 0 ? 'start' : i == nbTicks - 1 ? 'end' : 'middle')
|
|
100
122
|
.style('alignment-baseline', 'top')
|
|
101
123
|
.style('dominant-baseline', 'hanging')
|
|
102
124
|
.style('pointer-events', 'none')
|
|
@@ -107,8 +129,8 @@ export class ColorLegend extends Legend {
|
|
|
107
129
|
|
|
108
130
|
//label text format
|
|
109
131
|
const f = this.tickFormat && this.tickFormat != 'text' ? this.tickFormat : (v) => v
|
|
110
|
-
for (let i = 0; i <
|
|
111
|
-
let t = i / (
|
|
132
|
+
for (let i = 0; i < nbTicks; i++) {
|
|
133
|
+
let t = i / (nbTicks - 1)
|
|
112
134
|
|
|
113
135
|
const v = this.textScale(t, opts.viewScale)
|
|
114
136
|
const text = (v ? f(v) : '0') + (this.tickUnit ? this.tickUnit : '')
|