mirror of
https://github.com/apache/superset.git
synced 2026-04-21 17:14:57 +00:00
fix(deckgl-contour): prevent WebGL freeze by clamping and auto-scaling cellSize (#37244)
This commit is contained in:
@@ -25,6 +25,7 @@ import sandboxedEval from '../../utils/sandbox';
|
||||
import { GetLayerType, createDeckGLComponent } from '../../factory';
|
||||
import { ColorType } from '../../types';
|
||||
import TooltipRow from '../../TooltipRow';
|
||||
import { getSafeCellSize } from './getSafeCellSize';
|
||||
import {
|
||||
createTooltipContent,
|
||||
CommonTooltipRows,
|
||||
@@ -159,11 +160,23 @@ export const getLayer: GetLayerType<ContourLayer> = function ({
|
||||
return baseTooltipContent(o);
|
||||
};
|
||||
|
||||
const safeCellSize = getSafeCellSize({
|
||||
cellSize,
|
||||
viewport: fd.viewport,
|
||||
onAutoAdjust: ({ original, adjusted, estimatedCells }) => {
|
||||
console.warn(
|
||||
`[DeckGL Contour] cellSize=${original} would create ~${Math.round(
|
||||
estimatedCells,
|
||||
)} cells. Auto-adjusted to ${adjusted} to prevent WebGL crash.`,
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
return new ContourLayer({
|
||||
id: `contourLayer-${fd.slice_id}`,
|
||||
data,
|
||||
contours,
|
||||
cellSize: Number(cellSize || '200'),
|
||||
cellSize: safeCellSize,
|
||||
aggregation: aggregation.toUpperCase(),
|
||||
getPosition: (d: { position: number[]; weight: number }) =>
|
||||
d.position as Position,
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
import {
|
||||
getSafeCellSize,
|
||||
MIN_CELL_SIZE,
|
||||
MAX_CELL_SIZE,
|
||||
} from './getSafeCellSize';
|
||||
|
||||
describe('getSafeCellSize', () => {
|
||||
it('defaults to 200 when value is not finite', () => {
|
||||
expect(getSafeCellSize({ cellSize: 'nope' })).toBe(200);
|
||||
});
|
||||
|
||||
it('clamps below minimum', () => {
|
||||
expect(getSafeCellSize({ cellSize: 1 })).toBe(MIN_CELL_SIZE);
|
||||
});
|
||||
|
||||
it('clamps above maximum', () => {
|
||||
expect(getSafeCellSize({ cellSize: 999999 })).toBe(MAX_CELL_SIZE);
|
||||
});
|
||||
|
||||
it('auto-scales when estimated grid is too large', () => {
|
||||
const size = getSafeCellSize({
|
||||
cellSize: 1,
|
||||
viewport: { width: 11000, height: 11000 },
|
||||
});
|
||||
|
||||
expect(size).toBeGreaterThan(MIN_CELL_SIZE);
|
||||
});
|
||||
|
||||
it('never exceeds MAX_CELL_SIZE', () => {
|
||||
const size = getSafeCellSize({
|
||||
cellSize: 1,
|
||||
viewport: { width: 100000, height: 100000 },
|
||||
});
|
||||
|
||||
expect(size).toBeLessThanOrEqual(MAX_CELL_SIZE);
|
||||
});
|
||||
|
||||
it('calls onAutoAdjust when scaling happens', () => {
|
||||
const spy = jest.fn();
|
||||
|
||||
getSafeCellSize({
|
||||
cellSize: 1,
|
||||
viewport: { width: 11000, height: 11000 },
|
||||
onAutoAdjust: spy,
|
||||
});
|
||||
|
||||
expect(spy).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,73 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
export const MIN_CELL_SIZE = 10;
|
||||
export const MAX_CELL_SIZE = 5000;
|
||||
export const MAX_GRID_CELLS = 1_000_000;
|
||||
|
||||
export function getSafeCellSize({
|
||||
cellSize,
|
||||
viewport,
|
||||
onAutoAdjust,
|
||||
}: {
|
||||
cellSize?: string | number;
|
||||
viewport?: { width?: number; height?: number };
|
||||
onAutoAdjust?: (info: {
|
||||
original: number;
|
||||
adjusted: number;
|
||||
estimatedCells: number;
|
||||
}) => void;
|
||||
}) {
|
||||
let parsedCellSize = Number(cellSize ?? 200);
|
||||
if (!Number.isFinite(parsedCellSize)) {
|
||||
parsedCellSize = 200;
|
||||
}
|
||||
|
||||
let safeCellSize = Math.min(
|
||||
MAX_CELL_SIZE,
|
||||
Math.max(MIN_CELL_SIZE, parsedCellSize),
|
||||
);
|
||||
|
||||
if (
|
||||
viewport &&
|
||||
typeof viewport.width === 'number' &&
|
||||
typeof viewport.height === 'number' &&
|
||||
viewport.width > 0 &&
|
||||
viewport.height > 0
|
||||
) {
|
||||
const estimatedCells =
|
||||
(viewport.width / safeCellSize) * (viewport.height / safeCellSize);
|
||||
|
||||
if (estimatedCells > MAX_GRID_CELLS) {
|
||||
const scaleFactor = Math.sqrt(estimatedCells / MAX_GRID_CELLS);
|
||||
const adjustedCellSize = Math.ceil(safeCellSize * scaleFactor);
|
||||
|
||||
const finalSize = Math.min(MAX_CELL_SIZE, adjustedCellSize);
|
||||
|
||||
onAutoAdjust?.({
|
||||
original: safeCellSize,
|
||||
adjusted: finalSize,
|
||||
estimatedCells,
|
||||
});
|
||||
|
||||
safeCellSize = finalSize;
|
||||
}
|
||||
}
|
||||
|
||||
return safeCellSize;
|
||||
}
|
||||
@@ -51,3 +51,5 @@ export default class ContourChartPlugin extends ChartPlugin {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export { getSafeCellSize } from './getSafeCellSize';
|
||||
|
||||
Reference in New Issue
Block a user