mirror of
https://github.com/apache/superset.git
synced 2026-04-28 04:25:07 +00:00
fix(map-box): prevent clusters from being smaller than individual points (#38458)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
committed by
GitHub
parent
6f301707f9
commit
05b9970aa6
@@ -24,6 +24,10 @@ import roundDecimal from './utils/roundDecimal';
|
||||
import luminanceFromRGB from './utils/luminanceFromRGB';
|
||||
import 'mapbox-gl/dist/mapbox-gl.css';
|
||||
|
||||
// Shared radius bounds keep cluster and point sizing in sync.
|
||||
export const MIN_CLUSTER_RADIUS_RATIO = 1 / 6;
|
||||
export const MAX_POINT_RADIUS_RATIO = 1 / 3;
|
||||
|
||||
interface GeoJSONLocation {
|
||||
geometry: {
|
||||
coordinates: [number, number];
|
||||
@@ -181,13 +185,16 @@ class ScatterPlotGlowOverlay extends PureComponent<ScatterPlotGlowOverlayProps>
|
||||
}
|
||||
});
|
||||
|
||||
const filteredLabels = clusterLabelMap.filter(
|
||||
v => !Number.isNaN(v),
|
||||
) as number[];
|
||||
// Guard against empty array or zero max to prevent NaN from division
|
||||
const maxLabel =
|
||||
filteredLabels.length > 0 ? Math.max(...filteredLabels) : 1;
|
||||
const safeMaxLabel = maxLabel > 0 ? maxLabel : 1;
|
||||
const finiteClusterLabels = clusterLabelMap
|
||||
.map(value => Number(value))
|
||||
.filter(value => Number.isFinite(value));
|
||||
const safeMaxAbsLabel =
|
||||
finiteClusterLabels.length > 0
|
||||
? Math.max(
|
||||
Math.max(...finiteClusterLabels.map(value => Math.abs(value))),
|
||||
1,
|
||||
)
|
||||
: 1;
|
||||
|
||||
// Calculate min/max radius values for Pixels mode scaling
|
||||
let minRadiusValue = Infinity;
|
||||
@@ -200,8 +207,11 @@ class ScatterPlotGlowOverlay extends PureComponent<ScatterPlotGlowOverlayProps>
|
||||
location.properties.radius != null
|
||||
) {
|
||||
const radiusValueRaw = location.properties.radius;
|
||||
const radiusValue = Number(radiusValueRaw);
|
||||
if (Number.isFinite(radiusValue)) {
|
||||
const radiusValue =
|
||||
typeof radiusValueRaw === 'string' && radiusValueRaw.trim() === ''
|
||||
? null
|
||||
: Number(radiusValueRaw);
|
||||
if (radiusValue != null && Number.isFinite(radiusValue)) {
|
||||
minRadiusValue = Math.min(minRadiusValue, radiusValue);
|
||||
maxRadiusValue = Math.max(maxRadiusValue, radiusValue);
|
||||
}
|
||||
@@ -239,8 +249,13 @@ class ScatterPlotGlowOverlay extends PureComponent<ScatterPlotGlowOverlayProps>
|
||||
const safeNumericLabel = Number.isFinite(numericLabel)
|
||||
? numericLabel
|
||||
: 0;
|
||||
const minClusterRadius =
|
||||
pointRadiusUnit === 'Pixels'
|
||||
? radius * MAX_POINT_RADIUS_RATIO
|
||||
: radius * MIN_CLUSTER_RADIUS_RATIO;
|
||||
const ratio = Math.abs(safeNumericLabel) / safeMaxAbsLabel;
|
||||
const scaledRadius = roundDecimal(
|
||||
(safeNumericLabel / safeMaxLabel) ** 0.5 * radius,
|
||||
minClusterRadius + ratio ** 0.5 * (radius - minClusterRadius),
|
||||
1,
|
||||
);
|
||||
const fontHeight = roundDecimal(scaledRadius * 0.5, 1);
|
||||
@@ -274,10 +289,12 @@ class ScatterPlotGlowOverlay extends PureComponent<ScatterPlotGlowOverlayProps>
|
||||
|
||||
if (Number.isFinite(safeNumericLabel)) {
|
||||
let label: string | number = clusterLabel;
|
||||
if (safeNumericLabel >= 10000) {
|
||||
label = `${Math.round(safeNumericLabel / 1000)}k`;
|
||||
} else if (safeNumericLabel >= 1000) {
|
||||
label = `${Math.round(safeNumericLabel / 100) / 10}k`;
|
||||
const absLabel = Math.abs(safeNumericLabel);
|
||||
const sign = safeNumericLabel < 0 ? '-' : '';
|
||||
if (absLabel >= 10000) {
|
||||
label = `${sign}${Math.round(absLabel / 1000)}k`;
|
||||
} else if (absLabel >= 1000) {
|
||||
label = `${sign}${Math.round(absLabel / 100) / 10}k`;
|
||||
}
|
||||
this.drawText(ctx, pixelRounded, {
|
||||
fontHeight,
|
||||
@@ -288,10 +305,18 @@ class ScatterPlotGlowOverlay extends PureComponent<ScatterPlotGlowOverlayProps>
|
||||
});
|
||||
}
|
||||
} else {
|
||||
const defaultRadius = radius / 6;
|
||||
const defaultRadius = radius * MIN_CLUSTER_RADIUS_RATIO;
|
||||
const rawRadius = location.properties.radius;
|
||||
const numericRadiusProperty =
|
||||
rawRadius != null &&
|
||||
!(typeof rawRadius === 'string' && rawRadius.trim() === '')
|
||||
? Number(rawRadius)
|
||||
: null;
|
||||
const radiusProperty =
|
||||
typeof rawRadius === 'number' ? rawRadius : null;
|
||||
numericRadiusProperty != null &&
|
||||
Number.isFinite(numericRadiusProperty)
|
||||
? numericRadiusProperty
|
||||
: null;
|
||||
const pointMetric = location.properties.metric ?? null;
|
||||
let pointRadius: number = radiusProperty ?? defaultRadius;
|
||||
let pointLabel: string | number | undefined;
|
||||
@@ -311,8 +336,8 @@ class ScatterPlotGlowOverlay extends PureComponent<ScatterPlotGlowOverlayProps>
|
||||
} else if (pointRadiusUnit === 'Pixels') {
|
||||
// Scale pixel values to a reasonable range (radius/6 to radius/3)
|
||||
// This ensures points are visible and proportional to their values
|
||||
const MIN_POINT_RADIUS = radius / 6;
|
||||
const MAX_POINT_RADIUS = radius / 3;
|
||||
const MIN_POINT_RADIUS = radius * MIN_CLUSTER_RADIUS_RATIO;
|
||||
const MAX_POINT_RADIUS = radius * MAX_POINT_RADIUS_RATIO;
|
||||
|
||||
if (
|
||||
Number.isFinite(minRadiusValue) &&
|
||||
|
||||
Reference in New Issue
Block a user