Improvements to the polygon spatial viz (#6178)

* WIP

* WIP

* WIP

* WIP

* Fix color bucketing

* Fixed colors

* Fix no num categories selected

* Colors working

* Fix no metric selected

* Visual cues for selection

* Add unit tests

* Remove jest from deps

* Rename category to bucket

* Small fixes

* Fix lint

* Fix unit tests

* Remove duplicate hexToRGB

* Fix import

* Change order of arguments in getBuckets

* Refactor function signature
This commit is contained in:
Beto Dealmeida
2018-10-24 18:40:57 -07:00
committed by GitHub
parent ca5be1c1e2
commit f1089c40a4
17 changed files with 2372 additions and 42 deletions

View File

@@ -0,0 +1,92 @@
import d3 from 'd3';
import getSequentialSchemeRegistry from '../../modules/colors/SequentialSchemeRegistrySingleton';
import { colorScalerFactory, hexToRGB } from '../../modules/colors';
export function getBreakPoints({
break_points: formDataBreakPoints,
num_buckets: formDataNumBuckets,
metric,
}, features) {
if (formDataBreakPoints === undefined || formDataBreakPoints.length === 0) {
// compute evenly distributed break points based on number of buckets
const numBuckets = formDataNumBuckets
? parseInt(formDataNumBuckets, 10)
: 10;
const [minValue, maxValue] = d3.extent(features, d => d[metric]);
const delta = (maxValue - minValue) / numBuckets;
const precision = delta === 0
? 0
: Math.max(0, Math.ceil(Math.log10(1 / delta)));
return Array(numBuckets + 1)
.fill()
.map((_, i) => (minValue + i * delta).toFixed(precision));
}
return formDataBreakPoints.sort((a, b) => parseFloat(a) - parseFloat(b));
}
export function getBreakPointColorScaler({
break_points: formDataBreakPoints,
num_buckets: formDataNumBuckets,
linear_color_scheme: linearColorScheme,
metric,
opacity,
}, features) {
const breakPoints = formDataBreakPoints || formDataNumBuckets
? getBreakPoints({
break_points: formDataBreakPoints,
num_buckets: formDataNumBuckets,
metric,
}, features)
: null;
const colors = Array.isArray(linearColorScheme)
? linearColorScheme
: getSequentialSchemeRegistry().get(linearColorScheme).colors;
let scaler;
let maskPoint;
if (breakPoints !== null) {
// bucket colors into discrete colors
const colorScaler = colorScalerFactory(colors);
const n = breakPoints.length - 1;
const bucketedColors = n > 1
? [...Array(n).keys()].map(d => colorScaler(d / (n - 1)))
: [colors[colors.length - 1]];
// repeat ends
bucketedColors.unshift(bucketedColors[0]);
bucketedColors.push(bucketedColors[n - 1]);
const points = breakPoints.map(p => parseFloat(p));
scaler = d3.scale.threshold().domain(points).range(bucketedColors);
maskPoint = value => value > breakPoints[n] || value < breakPoints[0];
} else {
// interpolate colors linearly
scaler = colorScalerFactory(colors, features, d => d[metric]);
maskPoint = () => false;
}
return (d) => {
const c = hexToRGB(scaler(d[metric]));
if (maskPoint(d[metric])) {
c[3] = 0;
} else {
c[3] = (opacity / 100.0) * 255;
}
return c;
};
}
export function getBuckets(fd, features) {
const breakPoints = getBreakPoints(fd, features, true);
const colorScaler = getBreakPointColorScaler(fd, features);
const buckets = {};
breakPoints.slice(1).forEach((value, i) => {
const range = breakPoints[i] + ' - ' + breakPoints[i + 1];
const mid = 0.5 * (parseInt(breakPoints[i], 10) + parseInt(breakPoints[i + 1], 10));
buckets[range] = {
color: colorScaler({ [fd.metric]: mid }),
enabled: true,
};
});
return buckets;
}