Skip to content
Snippets Groups Projects
Unverified Commit 127b7eb8 authored by Paul Rosenzweig's avatar Paul Rosenzweig Committed by GitHub
Browse files

use compact formatting for long choropleth legends (#10654)

parent 04c4acb1
No related branches found
No related tags found
No related merge requests found
...@@ -69,6 +69,36 @@ function loadGeoJson(geoJsonPath, callback) { ...@@ -69,6 +69,36 @@ function loadGeoJson(geoJsonPath, callback) {
} }
} }
export function getLegendTitles(groups, columnSettings) {
const formatMetric = (value, compact) =>
formatValue(value, { ...columnSettings, compact });
const compact = shouldUseCompactFormatting(groups, formatMetric);
return groups.map((group, index) => {
const min = formatMetric(group[0], compact);
const max = formatMetric(group[group.length - 1], compact);
return index === groups.length - 1
? `${min} +` // the last value in the list
: min !== max
? `${min} - ${max}` // typical case
: min; // special case to avoid zero-width ranges e.g. $88-$88
});
}
// if the average formatted length is greater than this, we switch to compact formatting
const AVERAGE_LENGTH_CUTOFF = 5;
function shouldUseCompactFormatting(groups, formatMetric) {
const minValues = groups.map(([x]) => x);
const maxValues = groups.slice(0, -1).map(group => group[group.length - 1]);
const allValues = minValues.concat(maxValues);
const formattedValues = allValues.map(value => formatMetric(value, false));
const averageLength =
formattedValues.reduce((sum, { length }) => sum + length, 0) /
formattedValues.length;
return averageLength > AVERAGE_LENGTH_CUTOFF;
}
export default class ChoroplethMap extends Component { export default class ChoroplethMap extends Component {
static propTypes = {}; static propTypes = {};
...@@ -197,9 +227,6 @@ export default class ChoroplethMap extends Component { ...@@ -197,9 +227,6 @@ export default class ChoroplethMap extends Component {
const getFeatureValue = feature => valuesMap[getFeatureKey(feature)]; const getFeatureValue = feature => valuesMap[getFeatureKey(feature)];
const formatMetric = value =>
formatValue(value, settings.column(cols[metricIndex]));
const rowByFeatureKey = new Map(rows.map(row => [getRowKey(row), row])); const rowByFeatureKey = new Map(rows.map(row => [getRowKey(row), row]));
const getFeatureClickObject = (row, feature) => ({ const getFeatureClickObject = (row, feature) => ({
...@@ -253,10 +280,7 @@ export default class ChoroplethMap extends Component { ...@@ -253,10 +280,7 @@ export default class ChoroplethMap extends Component {
const domain = Array.from(domainSet); const domain = Array.from(domainSet);
const _heatMapColors = settings["map.colors"] || HEAT_MAP_COLORS; const _heatMapColors = settings["map.colors"] || HEAT_MAP_COLORS;
const heatMapColors = const heatMapColors = _heatMapColors.slice(-domain.length);
domain.length < _heatMapColors.length
? _heatMapColors.slice(_heatMapColors.length - domain.length)
: _heatMapColors;
const groups = ss.ckmeans(domain, heatMapColors.length); const groups = ss.ckmeans(domain, heatMapColors.length);
const groupBoundaries = groups.slice(1).map(cluster => cluster[0]); const groupBoundaries = groups.slice(1).map(cluster => cluster[0]);
...@@ -266,14 +290,8 @@ export default class ChoroplethMap extends Component { ...@@ -266,14 +290,8 @@ export default class ChoroplethMap extends Component {
.domain(groupBoundaries) .domain(groupBoundaries)
.range(heatMapColors); .range(heatMapColors);
const legendColors = heatMapColors; const columnSettings = settings.column(cols[metricIndex]);
const legendTitles = heatMapColors.map((color, index) => { const legendTitles = getLegendTitles(groups, columnSettings);
const min = groups[index][0];
const max = groups[index].slice(-1)[0];
return index === heatMapColors.length - 1
? formatMetric(min) + " +"
: formatMetric(min) + " - " + formatMetric(max);
});
const getColor = feature => { const getColor = feature => {
const value = getFeatureValue(feature); const value = getFeatureValue(feature);
...@@ -295,7 +313,7 @@ export default class ChoroplethMap extends Component { ...@@ -295,7 +313,7 @@ export default class ChoroplethMap extends Component {
className={className} className={className}
aspectRatio={aspectRatio} aspectRatio={aspectRatio}
legendTitles={legendTitles} legendTitles={legendTitles}
legendColors={legendColors} legendColors={heatMapColors}
gridSize={gridSize} gridSize={gridSize}
hovered={hovered} hovered={hovered}
onHoverChange={onHoverChange} onHoverChange={onHoverChange}
......
import { getLegendTitles } from "metabase/visualizations/components/ChoroplethMap";
describe("getLegendTitles", () => {
it("should not format short values compactly", () => {
const groups = [[1.12, 1.12, 1.25], [1.32, 1.48], [9, 12, 13]];
const columnSettings = {
column: { base_type: "type/Float" },
number_style: "currency",
currency: "USD",
currency_style: "symbol",
};
const titles = getLegendTitles(groups, columnSettings);
expect(titles).toEqual(["$1.12 - $1.25", "$1.32 - $1.48", "$9.00 +"]);
});
it("should format long values compactly", () => {
const groups = [
[1000.12, 1100.12, 1200.25],
[2000.32, 2200, 2500.48],
[11000, 12000, 13000],
];
const columnSettings = {
column: { base_type: "type/Float" },
number_style: "currency",
currency: "USD",
currency_style: "symbol",
};
const titles = getLegendTitles(groups, columnSettings);
expect(titles).toEqual(["$1.0k - $1.2k", "$2.0k - $2.5k", "$11.0k +"]);
});
});
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment