Compare commits

...

4 Commits

Author SHA1 Message Date
Evan Rusackas
728a0bb9e0 fix(lint): use destructuring syntax for oxlint compliance
Convert `const props = layer.props` to `const { props } = layer`
to satisfy oxlint's prefer-destructuring rule.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-04-27 10:02:34 -07:00
Evan Rusackas
3e188c2b13 test(deckgl): add backward compatibility tests for point radius
Add tests to verify that existing charts without the new point_radius
and point_radius_units fields get deck.gl defaults (preserving their
rendering), while new charts get the control panel defaults.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-04-27 10:02:34 -07:00
Evan Rusackas
91bdf68bde fix(deckgl): use deck.gl defaults for backward compatibility
Use deck.gl's built-in defaults (getPointRadius=1, units='meters') as
runtime fallbacks instead of the control panel defaults. This preserves
rendering for existing charts that don't have the new point_radius and
point_radius_units fields saved.

New charts will still get the improved UX defaults (point_radius=10,
units='pixels') from the control panel.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-04-27 10:02:33 -07:00
Evan Rusackas
097aee72ae feat(deckgl): add point radius controls for GeoJSON layer
Adds new controls to configure point radius for GeoJSON features,
addressing issue #31176 where users couldn't set circle sizes below
100 meters or use pixel-based sizing.

New controls:
- Point Radius: Set the base radius value (default: 10)
- Point Radius Units: Choose between pixels, meters, or common units
  (default: pixels for intuitive screen-space sizing)
- Point Radius Scale: Multiplier for uniform scaling (default: 1)

The controls map directly to deck.gl's GeoJsonLayer API:
- getPointRadius, pointRadiusUnits, pointRadiusScale

Fixes #31176

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-04-27 10:02:33 -07:00
3 changed files with 102 additions and 4 deletions

View File

@@ -22,6 +22,7 @@ import {
computeGeoJsonTextOptionsFromFormData,
computeGeoJsonIconOptionsFromJsOutput,
computeGeoJsonIconOptionsFromFormData,
getLayer,
} from './Geojson';
jest.mock('react-map-gl/maplibre', () => ({
@@ -120,3 +121,53 @@ test('computeGeoJsonIconOptionsFromFormData computes icon options based on form
width: 128,
});
});
const baseFormData: SqlaFormData = {
datasource: 'test_datasource',
viz_type: 'deck_geojson',
slice_id: 1,
fill_color_picker: { r: 0, g: 0, b: 255, a: 1 },
stroke_color_picker: { r: 0, g: 0, b: 0, a: 1 },
};
const baseLayerArgs = {
onContextMenu: jest.fn(),
filterState: undefined,
setDataMask: jest.fn(),
payload: { data: { type: 'FeatureCollection', features: [] } },
setTooltip: jest.fn(),
emitCrossFilters: false,
};
test('getLayer preserves rendering for existing charts without new point radius fields', () => {
// Simulate form data from an existing chart that only has point_radius_scale
const legacyFormData = {
...baseFormData,
point_radius_scale: 200,
// point_radius and point_radius_units intentionally absent
};
const layer = getLayer({ formData: legacyFormData, ...baseLayerArgs });
const { props } = layer;
// Should match deck.gl defaults, NOT the new control panel defaults
expect(props.getPointRadius).toBe(1); // deck.gl default, not 10
expect(props.pointRadiusUnits).toBe('meters'); // deck.gl default, not 'pixels'
expect(props.pointRadiusScale).toBe(200); // user's saved value preserved
});
test('getLayer uses control panel defaults for new charts', () => {
const newChartFormData = {
...baseFormData,
point_radius: 10,
point_radius_units: 'pixels',
point_radius_scale: 1,
};
const layer = getLayer({ formData: newChartFormData, ...baseLayerArgs });
const { props } = layer;
expect(props.getPointRadius).toBe(10);
expect(props.pointRadiusUnits).toBe('pixels');
expect(props.pointRadiusScale).toBe(1);
});

View File

@@ -326,7 +326,11 @@ export const getLayer: GetLayerType<GeoJsonLayer> = function ({
getFillColor(feature, filterState?.value),
getLineColor,
getLineWidth: fd.line_width || 1,
pointRadiusScale: fd.point_radius_scale,
// Use deck.gl defaults as fallbacks for backward compatibility with existing charts.
// New charts will get control panel defaults (point_radius=10, units='pixels', scale=1).
getPointRadius: fd.point_radius ?? 1,
pointRadiusUnits: fd.point_radius_units ?? 'meters',
pointRadiusScale: fd.point_radius_scale ?? 1,
lineWidthUnits: fd.line_width_unit,
pointType,
...labelOpts,

View File

@@ -22,6 +22,8 @@ import {
legacyValidateInteger,
isFeatureEnabled,
FeatureFlag,
validateNumber,
validateInteger,
} from '@superset-ui/core';
import { formatSelectOptions } from '../../utilities/utils';
import {
@@ -350,15 +352,56 @@ const config: ControlPanelConfig = {
},
],
[
{
name: 'point_radius',
config: {
type: 'SelectControl',
freeForm: true,
label: t('Point Radius'),
description: t(
'The radius of point features, in the units specified below. ' +
'The final rendered size is this value multiplied by Point Radius Scale.',
),
validators: [validateInteger],
default: 10,
choices: formatSelectOptions([1, 5, 10, 20, 50, 100]),
renderTrigger: true,
},
},
{
name: 'point_radius_scale',
config: {
type: 'SelectControl',
freeForm: true,
label: t('Point Radius Scale'),
validators: [legacyValidateInteger],
default: null,
choices: formatSelectOptions([0, 100, 200, 300, 500]),
description: t(
'A multiplier applied to the point radius. ' +
'Use this to uniformly scale all points.',
),
validators: [validateNumber],
default: 1,
choices: formatSelectOptions([0.1, 0.5, 1, 2, 5, 10]),
renderTrigger: true,
},
},
],
[
{
name: 'point_radius_units',
config: {
type: 'SelectControl',
label: t('Point Radius Units'),
description: t(
'The unit for point radius. Use "pixels" for consistent ' +
'screen-space sizing regardless of zoom level.',
),
default: 'pixels',
choices: [
['pixels', t('Pixels')],
['meters', t('Meters')],
['common', t('Common (unit per pixel at zoom 0)')],
],
renderTrigger: true,
},
},
],