Choropleth: allow to control breakpoints manually

This commit is contained in:
Yohan Boniface 2023-10-12 08:39:47 +02:00
parent 739626351c
commit 02342c487a
3 changed files with 42 additions and 16 deletions

View file

@ -354,7 +354,8 @@ input.switch:checked ~ label:after {
.button-bar.half { .button-bar.half {
grid-template-columns: 1fr 1fr; grid-template-columns: 1fr 1fr;
} }
.umap-multiplechoice.by3 { .umap-multiplechoice.by3,
.umap-multiplechoice.by5 {
grid-template-columns: 1fr 1fr 1fr; grid-template-columns: 1fr 1fr 1fr;
} }
.umap-multiplechoice.by4 { .umap-multiplechoice.by4 {

View file

@ -122,6 +122,7 @@ L.U.Layer.Choropleth = L.FeatureGroup.extend({
equidistant: L._('Equidistant'), equidistant: L._('Equidistant'),
jenks: L._('Jenks-Fisher'), jenks: L._('Jenks-Fisher'),
quantiles: L._('Quantiles'), quantiles: L._('Quantiles'),
manual: L._('Manual'),
}, },
initialize: function (datalayer) { initialize: function (datalayer) {
@ -159,20 +160,26 @@ L.U.Layer.Choropleth = L.FeatureGroup.extend({
return return
} }
let mode = this.datalayer.options.choropleth.mode, let mode = this.datalayer.options.choropleth.mode,
steps = +this.datalayer.options.choropleth.steps || 5, classes = +this.datalayer.options.choropleth.classes || 5,
breaks breaks = []
if (mode === 'equidistant') { if (mode === 'manual') {
breaks = ss.equalIntervalBreaks(values, steps) const manualBreaks = this.datalayer.options.choropleth.breaks
if (manualBreaks) {
breaks = manualBreaks.split(",").map(b => +b).filter(b => !isNaN(b))
}
} else if (mode === 'equidistant') {
breaks = ss.equalIntervalBreaks(values, classes)
} else if (mode === 'jenks') { } else if (mode === 'jenks') {
breaks = ss.jenks(values, steps) breaks = ss.jenks(values, classes)
} else if (mode === 'quantiles') { } else if (mode === 'quantiles') {
const quantiles = [...Array(steps)].map((e, i) => i/steps).concat(1) const quantiles = [...Array(classes)].map((e, i) => i/classes).concat(1)
breaks = ss.quantile(values, quantiles) breaks = ss.quantile(values, quantiles)
} else { } else {
breaks = ss.ckmeans(values, steps).map((cluster) => cluster[0]) breaks = ss.ckmeans(values, classes).map((cluster) => cluster[0])
breaks.push(ss.max(values)) // Needed for computing the legend breaks.push(ss.max(values)) // Needed for computing the legend
} }
this.options.breaks = breaks this.options.breaks = breaks
this.datalayer.options.choropleth.breaks = this.options.breaks.map(b => +b.toFixed(2)).join(',')
const fillColor = this.datalayer.getOption('fillColor') || this.defaults.fillColor const fillColor = this.datalayer.getOption('fillColor') || this.defaults.fillColor
let colorScheme = this.datalayer.options.choropleth.brewer let colorScheme = this.datalayer.options.choropleth.brewer
if (!colorbrewer[colorScheme]) colorScheme = 'Blues' if (!colorbrewer[colorScheme]) colorScheme = 'Blues'
@ -207,6 +214,17 @@ L.U.Layer.Choropleth = L.FeatureGroup.extend({
L.FeatureGroup.prototype.onAdd.call(this, map) L.FeatureGroup.prototype.onAdd.call(this, map)
}, },
postUpdate: function (e) {
if (e.helper.field === 'options.choropleth.breaks') {
this.datalayer.options.choropleth.mode = 'manual'
e.helper.builder.helpers["options.choropleth.mode"].fetch()
}
this.computeBreaks()
if (e.helper.field !== 'options.choropleth.breaks') {
e.helper.builder.helpers["options.choropleth.breaks"].fetch()
}
},
getEditableOptions: function () { getEditableOptions: function () {
const brewerSchemes = Object.keys(colorbrewer) const brewerSchemes = Object.keys(colorbrewer)
.filter((k) => k !== 'schemeGroups') .filter((k) => k !== 'schemeGroups')
@ -218,8 +236,7 @@ L.U.Layer.Choropleth = L.FeatureGroup.extend({
{ {
handler: 'Select', handler: 'Select',
selectOptions: this.datalayer._propertiesIndex, selectOptions: this.datalayer._propertiesIndex,
placeholder: L._('Choropleth property value'), label: L._('Choropleth property value'),
helpText: L._('Choropleth property value'),
}, },
], ],
[ [
@ -231,14 +248,22 @@ L.U.Layer.Choropleth = L.FeatureGroup.extend({
}, },
], ],
[ [
'options.choropleth.steps', 'options.choropleth.classes',
{ {
handler: 'Range', handler: 'Range',
min: 3, min: 3,
max: 9, max: 9,
step: 1, step: 1,
placeholder: L._('Choropleth steps'), label: L._('Choropleth classes'),
helpText: L._('Choropleth steps (default 5)'), helpText: L._('Number of desired classes (default 5)'),
},
],
[
'options.choropleth.breaks',
{
handler: 'BlurInput',
label: L._('Choropleth breakpoints'),
helpText: L._('Comma separated list of numbers, including min and max values.'),
}, },
], ],
[ [
@ -1060,9 +1085,9 @@ L.U.DataLayer = L.Evented.extend({
'options.fillOpacity', 'options.fillOpacity',
] ]
const redrawCallback = function (field) { const redrawCallback = function (e) {
this.hide() this.hide()
this.layer.postUpdate(field) this.layer.postUpdate(e)
this.show() this.show()
} }

View file

@ -233,7 +233,7 @@ describe('L.U.Choropleth', function () {
}) })
it('choropleth should allow to change steps', function () { it('choropleth should allow to change steps', function () {
this.datalayer.options.choropleth.brewer = 'Blues' this.datalayer.options.choropleth.brewer = 'Blues'
this.datalayer.options.choropleth.steps = 6 this.datalayer.options.choropleth.classes = 6
this.datalayer.resetLayer(true) this.datalayer.resetLayer(true)
assert.equal(poly1._path.attributes.fill.value, '#eff3ff') assert.equal(poly1._path.attributes.fill.value, '#eff3ff')
assert.equal(poly4._path.attributes.fill.value, '#c6dbef') assert.equal(poly4._path.attributes.fill.value, '#c6dbef')