codeant-ai-for-open-source[bot] commented on code in PR #35525:
URL: https://github.com/apache/superset/pull/35525#discussion_r2658836548


##########
superset-frontend/plugins/legacy-plugin-chart-map-box/src/MapBox.tsx:
##########
@@ -0,0 +1,227 @@
+/**
+ * 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 { Component } from 'react';
+import Map, { ViewStateChangeEvent } from 'react-map-gl/mapbox';
+import { WebMercatorViewport } from '@math.gl/web-mercator';
+import ScatterPlotGlowOverlay, {
+  type AggregationType,
+  type Location,
+} from './ScatterPlotGlowOverlay';
+import './MapBox.css';
+
+export interface Clusterer {
+  getClusters(bbox: [number, number, number, number], zoom: number): 
Location[];
+}
+
+const NOOP = () => {};
+export const DEFAULT_MAX_ZOOM = 16;
+export const DEFAULT_POINT_RADIUS = 60;
+const DEFAULT_DIMENSION = 400;
+const VIEWPORT_BUFFER_MULTIPLIER = 0.5;
+const VIEWPORT_BUFFER_DIVISOR = 100;
+
+interface Viewport {
+  longitude: number;
+  latitude: number;
+  zoom: number;
+  isDragging?: boolean;
+}
+
+interface MapBoxProps {
+  aggregatorName?: AggregationType;
+  bounds: [[number, number], [number, number]];
+  clusterer: Clusterer;
+  globalOpacity?: number;
+  hasCustomMetric?: boolean;
+  height?: number;
+  mapStyle?: string;
+  mapboxApiKey: string;
+  onViewportChange?: (viewport: Viewport) => void;
+  pointRadius?: number;
+  pointRadiusUnit?: string;
+  renderWhileDragging?: boolean;
+  rgb?: [number, number, number, number];
+  width?: number;
+}
+
+interface MapBoxState {
+  viewport: Viewport;
+  clusters: Location[];
+  isMapLoaded: boolean;
+}
+
+const defaultProps: Partial<MapBoxProps> = {
+  width: 400,
+  height: 400,
+  globalOpacity: 1,
+  onViewportChange: NOOP,
+  pointRadius: DEFAULT_POINT_RADIUS,
+  pointRadiusUnit: 'Pixels',
+};
+
+class MapBox extends Component<MapBoxProps, MapBoxState> {
+  static defaultProps = defaultProps;
+
+  private lastZoom: number;
+
+  constructor(props: MapBoxProps) {
+    super(props);
+
+    const { width, height, bounds, clusterer } = this.props;
+    const mercator = new WebMercatorViewport({
+      width: width || DEFAULT_DIMENSION,
+      height: height || DEFAULT_DIMENSION,
+    }).fitBounds(bounds);
+    const { latitude, longitude, zoom } = mercator;
+
+    const viewport = { longitude, latitude, zoom };
+    this.lastZoom = Math.round(zoom);
+
+    this.state = {
+      viewport,
+      clusters: this.computeClusters(clusterer, bounds, width, height, zoom),
+      isMapLoaded: false,
+    };
+    this.handleViewportChange = this.handleViewportChange.bind(this);
+    this.handleMapLoad = this.handleMapLoad.bind(this);
+  }
+
+  componentDidUpdate(prevProps: MapBoxProps, prevState: MapBoxState) {
+    const { viewport } = this.state;
+    const { clusterer, bounds, width, height } = this.props;
+    const roundedZoom = Math.round(viewport.zoom);
+
+    const shouldRecompute =
+      prevProps.clusterer !== clusterer || this.lastZoom !== roundedZoom;
+
+    if (shouldRecompute) {
+      this.lastZoom = roundedZoom;
+      this.setState({
+        clusters: this.computeClusters(
+          clusterer,
+          bounds,
+          width,
+          height,
+          viewport.zoom,
+        ),
+      });
+    }
+  }
+
+  computeClusters(
+    clusterer: Clusterer,
+    bounds: [[number, number], [number, number]],
+    width: number | undefined,
+    height: number | undefined,
+    zoom: number,
+  ): Location[] {
+    const offsetHorizontal =
+      ((width || DEFAULT_DIMENSION) * VIEWPORT_BUFFER_MULTIPLIER) /
+      VIEWPORT_BUFFER_DIVISOR;
+    const offsetVertical =
+      ((height || DEFAULT_DIMENSION) * VIEWPORT_BUFFER_MULTIPLIER) /
+      VIEWPORT_BUFFER_DIVISOR;
+    const bbox: [number, number, number, number] = [
+      bounds[0][0] - offsetHorizontal,
+      bounds[0][1] - offsetVertical,
+      bounds[1][0] + offsetHorizontal,
+      bounds[1][1] + offsetVertical,
+    ];
+    return clusterer.getClusters(bbox, Math.round(zoom));
+  }
+
+  handleViewportChange(evt: ViewStateChangeEvent) {
+    const { latitude, longitude, zoom } = evt.viewState;
+
+    if (
+      latitude === undefined ||
+      longitude === undefined ||
+      zoom === undefined
+    ) {
+      console.warn('MapBox: Invalid viewport change event', evt);
+      return;
+    }
+
+    const viewport: Viewport = { latitude, longitude, zoom };

Review Comment:
   **Suggestion:** handleViewportChange drops the `isDragging` flag from the 
incoming `evt.viewState` and stores a viewport object without it; as a result 
`viewport.isDragging` never becomes true while the map is being dragged and 
child overlay logic that relies on `isDragging` (e.g. render optimizations) 
will not behave correctly. Preserve and store `isDragging` from `evt.viewState` 
when updating the component state and forwarding the viewport. [logic error]
   
   **Severity Level:** Minor ⚠️
   ```suggestion
       const { latitude, longitude, zoom, isDragging } = evt.viewState;
   
       if (
         latitude === undefined ||
         longitude === undefined ||
         zoom === undefined
       ) {
         console.warn('MapBox: Invalid viewport change event', evt);
         return;
       }
   
       const viewport: Viewport = { latitude, longitude, zoom, isDragging };
   ```
   <details>
   <summary><b>Why it matters? ⭐ </b></summary>
   
   Accurate and actionable. The current code never preserves isDragging from 
the Map's viewState, so consumers relying on viewport.isDragging (or the 
component's own derived isDragging) will always see false. Including isDragging 
in the stored viewport fixes a real logic bug and is a minimal, safe change 
that matches the existing Viewport type and onViewportChange contract.
   </details>
   <details>
   <summary><b>Prompt for AI Agent 🤖 </b></summary>
   
   ```mdx
   This is a comment left during a code review.
   
   **Path:** 
superset-frontend/plugins/legacy-plugin-chart-map-box/src/MapBox.tsx
   **Line:** 150:161
   **Comment:**
        *Logic Error: handleViewportChange drops the `isDragging` flag from the 
incoming `evt.viewState` and stores a viewport object without it; as a result 
`viewport.isDragging` never becomes true while the map is being dragged and 
child overlay logic that relies on `isDragging` (e.g. render optimizations) 
will not behave correctly. Preserve and store `isDragging` from `evt.viewState` 
when updating the component state and forwarding the viewport.
   
   Validate the correctness of the flagged issue. If correct, How can I resolve 
this? If you propose a fix, implement it and please make it concise.
   ```
   </details>



##########
superset-frontend/plugins/legacy-plugin-chart-map-box/src/ScatterPlotGlowOverlay.tsx:
##########
@@ -0,0 +1,460 @@
+/**
+ * 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 { PureComponent, createRef, RefObject } from 'react';
+import { useMap } from 'react-map-gl/mapbox';
+import type { MapRef } from 'react-map-gl/mapbox';
+import { kmToPixels, MILES_PER_KM } from './utils/geo';
+import roundDecimal from './utils/roundDecimal';
+import luminanceFromRGB from './utils/luminanceFromRGB';
+import 'mapbox-gl/dist/mapbox-gl.css';
+
+const LUMINANCE_THRESHOLD_DARK = 110;
+const TEXT_WIDTH_RATIO = 1.8;
+const DEFAULT_RGB_COLOR: [number, number, number, number] = [0, 0, 0, 0];
+const DEFAULT_DOT_RADIUS = 4;
+const DEFAULT_RADIUS_DIVISOR = 6;
+const CLUSTER_LABEL_THRESHOLD_LARGE = 10000;
+const CLUSTER_LABEL_THRESHOLD_MEDIUM = 1000;
+
+export type AggregationType = 'sum' | 'min' | 'max' | 'mean' | 'var' | 'stdev';
+
+export interface ClusterProperties {
+  cluster: true;
+  cluster_id?: number;
+  point_count: number;
+  sum?: number;
+  squaredSum?: number;
+  min?: number;
+  max?: number;
+}
+
+export interface PointProperties {
+  cluster?: false;
+  radius?: number | null;
+  metric?: number | string | null;
+  cat_color?: string | null;
+}
+
+export type LocationProperties = ClusterProperties | PointProperties;
+
+export interface Geometry {
+  coordinates: [number, number];
+  type: string;
+}
+
+export interface Location {
+  type?: 'Feature';
+  geometry: Geometry;
+  properties: LocationProperties;
+}
+
+interface ScatterPlotGlowOverlayProps {
+  aggregation?: AggregationType;
+  compositeOperation?: GlobalCompositeOperation;
+  dotRadius?: number;
+  isDragging?: boolean;
+  lngLatAccessor?: (location: Location) => [number, number];
+  locations: Location[];
+  mapRef?: { current: MapRef | null | undefined };
+  pointRadiusUnit?: string;
+  renderWhileDragging?: boolean;
+  rgb?: [number, number, number, number];
+  zoom?: number;
+}
+
+interface DrawTextOptions {
+  fontHeight?: number;
+  label?: string | number;
+  radius?: number;
+  rgb?: [number, number, number, number];
+  shadow?: boolean;
+}
+
+const defaultProps: Partial<ScatterPlotGlowOverlayProps> = {
+  // Same as browser default.
+  compositeOperation: 'source-over',
+  dotRadius: 4,
+  lngLatAccessor: (location: Location) => {
+    const { coordinates } = location.geometry;
+    return [coordinates[0], coordinates[1]];
+  },
+  renderWhileDragging: true,
+};
+
+const isClusterProperties = (
+  props: LocationProperties,
+): props is ClusterProperties => props.cluster === true;
+
+const computeClusterLabel = (
+  properties: LocationProperties,
+  aggregation?: AggregationType,
+): number => {
+  if (!isClusterProperties(properties)) {
+    return 0;
+  }
+
+  const count = properties.point_count ?? 0;
+  if (!aggregation) {
+    return count;
+  }
+  if (aggregation === 'sum' || aggregation === 'min' || aggregation === 'max') 
{
+    const value = properties[aggregation];
+    if (typeof value === 'number') {
+      return value;
+    }
+    return 0;
+  }
+  const sum = properties.sum ?? 0;
+  const mean = count > 0 ? sum / count : 0;
+  if (aggregation === 'mean') {
+    return Math.round(100 * mean) / 100;
+  }
+  const squaredSum = properties.squaredSum ?? 0;
+  const variance = count > 0 ? squaredSum / count - mean ** 2 : 0;
+  if (aggregation === 'var') {
+    return Math.round(100 * variance) / 100;
+  }
+  if (aggregation === 'stdev') {
+    return Math.round(100 * Math.sqrt(Math.max(0, variance))) / 100;
+  }
+
+  return count;
+};
+
+class ScatterPlotGlowOverlay extends 
PureComponent<ScatterPlotGlowOverlayProps> {
+  static defaultProps = defaultProps;
+
+  private canvasRef: RefObject<HTMLCanvasElement>;
+
+  private containerRef: RefObject<HTMLDivElement>;
+
+  constructor(props: ScatterPlotGlowOverlayProps) {
+    super(props);
+    this.canvasRef = createRef<HTMLCanvasElement>();
+    this.containerRef = createRef<HTMLDivElement>();
+    this.redraw = this.redraw.bind(this);
+    this.updateCanvasSize = this.updateCanvasSize.bind(this);
+  }
+
+  componentDidMount() {
+    this.updateCanvasSize();
+    this.redraw();
+    if (this.props.mapRef?.current) {
+      this.props.mapRef.current.on('move', this.redraw);
+      this.props.mapRef.current.on('moveend', this.redraw);
+    }
+  }
+
+  componentDidUpdate(prevProps: ScatterPlotGlowOverlayProps) {
+    const shouldUpdateSize = prevProps.isDragging !== this.props.isDragging;
+    if (shouldUpdateSize) {
+      this.updateCanvasSize();
+    }
+
+    const shouldRedraw =
+      prevProps.locations !== this.props.locations ||
+      prevProps.dotRadius !== this.props.dotRadius ||
+      prevProps.rgb !== this.props.rgb ||
+      prevProps.aggregation !== this.props.aggregation ||
+      prevProps.compositeOperation !== this.props.compositeOperation ||
+      prevProps.pointRadiusUnit !== this.props.pointRadiusUnit ||
+      prevProps.zoom !== this.props.zoom;
+
+    if (shouldRedraw) {
+      this.redraw();
+    }
+  }
+
+  componentWillUnmount() {
+    if (this.props.mapRef?.current) {
+      this.props.mapRef.current.off('move', this.redraw);
+      this.props.mapRef.current.off('moveend', this.redraw);
+    }
+  }
+
+  updateCanvasSize() {
+    const canvas = this.canvasRef.current;
+    const container = this.containerRef.current;
+    if (!canvas || !container) return;
+
+    const { width, height } = container.getBoundingClientRect();
+    if (canvas.width !== width || canvas.height !== height) {
+      canvas.width = width;
+      canvas.height = height;

Review Comment:
   **Suggestion:** Canvas size is set using floating bounding-box dimensions 
directly which can cause frequent unnecessary resizes and rendering issues; 
round sizes and also set the element's CSS size to avoid mismatches (and 
optionally account for devicePixelRatio elsewhere). [possible bug]
   
   **Severity Level:** Critical 🚨
   ```suggestion
       const w = Math.round(width);
       const h = Math.round(height);
       // Keep canvas internal pixel size in whole pixels and keep the CSS size 
in sync.
       if (canvas.width !== w || canvas.height !== h) {
         canvas.width = w;
         canvas.height = h;
         canvas.style.width = `${w}px`;
         canvas.style.height = `${h}px`;
   ```
   <details>
   <summary><b>Why it matters? ⭐ </b></summary>
   
   Rounding bounding-box dimensions and keeping the canvas element's CSS size 
in sync reduces pointless resize churn when getBoundingClientRect returns 
fractional pixels and avoids layout vs. bitmap-size mismatches. The suggested 
change is a sensible, low-risk improvement over the current logic.
   </details>
   <details>
   <summary><b>Prompt for AI Agent 🤖 </b></summary>
   
   ```mdx
   This is a comment left during a code review.
   
   **Path:** 
superset-frontend/plugins/legacy-plugin-chart-map-box/src/ScatterPlotGlowOverlay.tsx
   **Line:** 197:199
   **Comment:**
        *Possible Bug: Canvas size is set using floating bounding-box 
dimensions directly which can cause frequent unnecessary resizes and rendering 
issues; round sizes and also set the element's CSS size to avoid mismatches 
(and optionally account for devicePixelRatio elsewhere).
   
   Validate the correctness of the flagged issue. If correct, How can I resolve 
this? If you propose a fix, implement it and please make it concise.
   ```
   </details>



##########
superset-frontend/plugins/legacy-plugin-chart-map-box/src/MapBox.tsx:
##########
@@ -0,0 +1,227 @@
+/**
+ * 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 { Component } from 'react';
+import Map, { ViewStateChangeEvent } from 'react-map-gl/mapbox';
+import { WebMercatorViewport } from '@math.gl/web-mercator';
+import ScatterPlotGlowOverlay, {
+  type AggregationType,
+  type Location,
+} from './ScatterPlotGlowOverlay';
+import './MapBox.css';
+
+export interface Clusterer {
+  getClusters(bbox: [number, number, number, number], zoom: number): 
Location[];
+}
+
+const NOOP = () => {};
+export const DEFAULT_MAX_ZOOM = 16;
+export const DEFAULT_POINT_RADIUS = 60;
+const DEFAULT_DIMENSION = 400;
+const VIEWPORT_BUFFER_MULTIPLIER = 0.5;
+const VIEWPORT_BUFFER_DIVISOR = 100;
+
+interface Viewport {
+  longitude: number;
+  latitude: number;
+  zoom: number;
+  isDragging?: boolean;
+}
+
+interface MapBoxProps {
+  aggregatorName?: AggregationType;
+  bounds: [[number, number], [number, number]];
+  clusterer: Clusterer;
+  globalOpacity?: number;
+  hasCustomMetric?: boolean;
+  height?: number;
+  mapStyle?: string;
+  mapboxApiKey: string;
+  onViewportChange?: (viewport: Viewport) => void;
+  pointRadius?: number;
+  pointRadiusUnit?: string;
+  renderWhileDragging?: boolean;
+  rgb?: [number, number, number, number];
+  width?: number;
+}
+
+interface MapBoxState {
+  viewport: Viewport;
+  clusters: Location[];
+  isMapLoaded: boolean;
+}
+
+const defaultProps: Partial<MapBoxProps> = {
+  width: 400,
+  height: 400,
+  globalOpacity: 1,
+  onViewportChange: NOOP,
+  pointRadius: DEFAULT_POINT_RADIUS,
+  pointRadiusUnit: 'Pixels',
+};
+
+class MapBox extends Component<MapBoxProps, MapBoxState> {
+  static defaultProps = defaultProps;
+
+  private lastZoom: number;
+
+  constructor(props: MapBoxProps) {
+    super(props);
+
+    const { width, height, bounds, clusterer } = this.props;
+    const mercator = new WebMercatorViewport({
+      width: width || DEFAULT_DIMENSION,
+      height: height || DEFAULT_DIMENSION,
+    }).fitBounds(bounds);
+    const { latitude, longitude, zoom } = mercator;
+
+    const viewport = { longitude, latitude, zoom };
+    this.lastZoom = Math.round(zoom);
+
+    this.state = {
+      viewport,
+      clusters: this.computeClusters(clusterer, bounds, width, height, zoom),
+      isMapLoaded: false,
+    };
+    this.handleViewportChange = this.handleViewportChange.bind(this);
+    this.handleMapLoad = this.handleMapLoad.bind(this);
+  }
+
+  componentDidUpdate(prevProps: MapBoxProps, prevState: MapBoxState) {
+    const { viewport } = this.state;
+    const { clusterer, bounds, width, height } = this.props;
+    const roundedZoom = Math.round(viewport.zoom);
+
+    const shouldRecompute =
+      prevProps.clusterer !== clusterer || this.lastZoom !== roundedZoom;

Review Comment:
   **Suggestion:** componentDidUpdate only recomputes clusters when the 
`clusterer` prop changes or zoom crosses a rounded threshold; it ignores 
changes to `bounds`, `width`, or `height`. If the bounding box or component 
size changes the clusters will become stale. Add checks for bounds and size 
changes (deep-compare bounds) so clusters are recomputed when those inputs 
change. [logic error]
   
   **Severity Level:** Minor ⚠️
   ```suggestion
       const boundsChanged =
         JSON.stringify(prevProps.bounds) !== JSON.stringify(bounds);
       const sizeChanged = prevProps.width !== width || prevProps.height !== 
height;
   
       const shouldRecompute =
         prevProps.clusterer !== clusterer ||
         this.lastZoom !== roundedZoom ||
         boundsChanged ||
         sizeChanged;
   ```
   <details>
   <summary><b>Why it matters? ⭐ </b></summary>
   
   Correct and relevant. computeClusters depends on bounds, width and height; 
componentDidUpdate currently ignores changes to those props, which can produce 
stale clusters after resize or bounds update. Adding bounds/size comparisons 
ensures clusters are recomputed when their inputs change. The suggested 
JSON.stringify check is pragmatic for a shallow deep-compare of the bounds 
tuple.
   </details>
   <details>
   <summary><b>Prompt for AI Agent 🤖 </b></summary>
   
   ```mdx
   This is a comment left during a code review.
   
   **Path:** 
superset-frontend/plugins/legacy-plugin-chart-map-box/src/MapBox.tsx
   **Line:** 110:111
   **Comment:**
        *Logic Error: componentDidUpdate only recomputes clusters when the 
`clusterer` prop changes or zoom crosses a rounded threshold; it ignores 
changes to `bounds`, `width`, or `height`. If the bounding box or component 
size changes the clusters will become stale. Add checks for bounds and size 
changes (deep-compare bounds) so clusters are recomputed when those inputs 
change.
   
   Validate the correctness of the flagged issue. If correct, How can I resolve 
this? If you propose a fix, implement it and please make it concise.
   ```
   </details>



##########
superset-frontend/plugins/legacy-plugin-chart-map-box/src/index.ts:
##########
@@ -24,14 +24,15 @@ import example1Dark from './images/MapBox-dark.jpg';
 import example2 from './images/MapBox2.jpg';
 import example2Dark from './images/MapBox2-dark.jpg';
 import controlPanel from './controlPanel';
+import transformProps from './transformProps';
 
 const metadata = new ChartMetadata({
   category: t('Map'),
   credits: ['https://www.mapbox.com/mapbox-gl-js/api/'],
   description: '',
   exampleGallery: [
-    { url: example1, urlDark: example1Dark, description: t('Light mode') },
-    { url: example2, urlDark: example2Dark, description: t('Dark mode') },
+    { url: example1, urlDark: example1Dark },
+    { url: example2, urlDark: example2Dark },

Review Comment:
   **Suggestion:** Performance: importing large example images at module 
top-level forces bundlers to include them in the initial bundle; adding 
multiple large images will bloat the main bundle. Use the already-imported 
small `thumbnail`/`thumbnailDark` for the metadata gallery or lazy-load the 
heavy images to keep initial bundle size small. [performance]
   
   **Severity Level:** Minor ⚠️
   ```suggestion
       { url: thumbnail, urlDark: thumbnailDark },
       { url: thumbnail, urlDark: thumbnailDark },
   ```
   <details>
   <summary><b>Why it matters? ⭐ </b></summary>
   
   Valid performance concern: importing heavy images at module top-level can 
bloat the initial bundle. Replacing gallery entries with smaller thumbnails or 
lazy-loading the large examples will reduce bundle size. The suggested 
replacement (using thumbnail) is reasonable, though duplicating the same 
thumbnail twice is odd — better to use distinct smaller assets or lazy-load the 
originals when the gallery is opened.
   </details>
   <details>
   <summary><b>Prompt for AI Agent 🤖 </b></summary>
   
   ```mdx
   This is a comment left during a code review.
   
   **Path:** superset-frontend/plugins/legacy-plugin-chart-map-box/src/index.ts
   **Line:** 34:35
   **Comment:**
        *Performance: Performance: importing large example images at module 
top-level forces bundlers to include them in the initial bundle; adding 
multiple large images will bloat the main bundle. Use the already-imported 
small `thumbnail`/`thumbnailDark` for the metadata gallery or lazy-load the 
heavy images to keep initial bundle size small.
   
   Validate the correctness of the flagged issue. If correct, How can I resolve 
this? If you propose a fix, implement it and please make it concise.
   ```
   </details>



##########
superset-frontend/plugins/legacy-plugin-chart-map-box/src/ScatterPlotGlowOverlay.tsx:
##########
@@ -0,0 +1,460 @@
+/**
+ * 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 { PureComponent, createRef, RefObject } from 'react';
+import { useMap } from 'react-map-gl/mapbox';
+import type { MapRef } from 'react-map-gl/mapbox';
+import { kmToPixels, MILES_PER_KM } from './utils/geo';
+import roundDecimal from './utils/roundDecimal';
+import luminanceFromRGB from './utils/luminanceFromRGB';
+import 'mapbox-gl/dist/mapbox-gl.css';
+
+const LUMINANCE_THRESHOLD_DARK = 110;
+const TEXT_WIDTH_RATIO = 1.8;
+const DEFAULT_RGB_COLOR: [number, number, number, number] = [0, 0, 0, 0];
+const DEFAULT_DOT_RADIUS = 4;
+const DEFAULT_RADIUS_DIVISOR = 6;
+const CLUSTER_LABEL_THRESHOLD_LARGE = 10000;
+const CLUSTER_LABEL_THRESHOLD_MEDIUM = 1000;
+
+export type AggregationType = 'sum' | 'min' | 'max' | 'mean' | 'var' | 'stdev';
+
+export interface ClusterProperties {
+  cluster: true;
+  cluster_id?: number;
+  point_count: number;
+  sum?: number;
+  squaredSum?: number;
+  min?: number;
+  max?: number;
+}
+
+export interface PointProperties {
+  cluster?: false;
+  radius?: number | null;
+  metric?: number | string | null;
+  cat_color?: string | null;
+}
+
+export type LocationProperties = ClusterProperties | PointProperties;
+
+export interface Geometry {
+  coordinates: [number, number];
+  type: string;
+}
+
+export interface Location {
+  type?: 'Feature';
+  geometry: Geometry;
+  properties: LocationProperties;
+}
+
+interface ScatterPlotGlowOverlayProps {
+  aggregation?: AggregationType;
+  compositeOperation?: GlobalCompositeOperation;
+  dotRadius?: number;
+  isDragging?: boolean;
+  lngLatAccessor?: (location: Location) => [number, number];
+  locations: Location[];
+  mapRef?: { current: MapRef | null | undefined };
+  pointRadiusUnit?: string;
+  renderWhileDragging?: boolean;
+  rgb?: [number, number, number, number];
+  zoom?: number;
+}
+
+interface DrawTextOptions {
+  fontHeight?: number;
+  label?: string | number;
+  radius?: number;
+  rgb?: [number, number, number, number];
+  shadow?: boolean;
+}
+
+const defaultProps: Partial<ScatterPlotGlowOverlayProps> = {
+  // Same as browser default.
+  compositeOperation: 'source-over',
+  dotRadius: 4,
+  lngLatAccessor: (location: Location) => {
+    const { coordinates } = location.geometry;
+    return [coordinates[0], coordinates[1]];
+  },
+  renderWhileDragging: true,
+};
+
+const isClusterProperties = (
+  props: LocationProperties,
+): props is ClusterProperties => props.cluster === true;
+
+const computeClusterLabel = (
+  properties: LocationProperties,
+  aggregation?: AggregationType,
+): number => {
+  if (!isClusterProperties(properties)) {
+    return 0;
+  }
+
+  const count = properties.point_count ?? 0;
+  if (!aggregation) {
+    return count;
+  }
+  if (aggregation === 'sum' || aggregation === 'min' || aggregation === 'max') 
{
+    const value = properties[aggregation];
+    if (typeof value === 'number') {
+      return value;
+    }
+    return 0;
+  }
+  const sum = properties.sum ?? 0;
+  const mean = count > 0 ? sum / count : 0;
+  if (aggregation === 'mean') {
+    return Math.round(100 * mean) / 100;
+  }
+  const squaredSum = properties.squaredSum ?? 0;
+  const variance = count > 0 ? squaredSum / count - mean ** 2 : 0;
+  if (aggregation === 'var') {
+    return Math.round(100 * variance) / 100;
+  }
+  if (aggregation === 'stdev') {
+    return Math.round(100 * Math.sqrt(Math.max(0, variance))) / 100;
+  }
+
+  return count;
+};
+
+class ScatterPlotGlowOverlay extends 
PureComponent<ScatterPlotGlowOverlayProps> {
+  static defaultProps = defaultProps;
+
+  private canvasRef: RefObject<HTMLCanvasElement>;
+
+  private containerRef: RefObject<HTMLDivElement>;
+
+  constructor(props: ScatterPlotGlowOverlayProps) {
+    super(props);
+    this.canvasRef = createRef<HTMLCanvasElement>();
+    this.containerRef = createRef<HTMLDivElement>();
+    this.redraw = this.redraw.bind(this);
+    this.updateCanvasSize = this.updateCanvasSize.bind(this);
+  }
+
+  componentDidMount() {
+    this.updateCanvasSize();
+    this.redraw();
+    if (this.props.mapRef?.current) {
+      this.props.mapRef.current.on('move', this.redraw);
+      this.props.mapRef.current.on('moveend', this.redraw);
+    }
+  }
+
+  componentDidUpdate(prevProps: ScatterPlotGlowOverlayProps) {
+    const shouldUpdateSize = prevProps.isDragging !== this.props.isDragging;
+    if (shouldUpdateSize) {
+      this.updateCanvasSize();
+    }
+
+    const shouldRedraw =
+      prevProps.locations !== this.props.locations ||
+      prevProps.dotRadius !== this.props.dotRadius ||
+      prevProps.rgb !== this.props.rgb ||
+      prevProps.aggregation !== this.props.aggregation ||
+      prevProps.compositeOperation !== this.props.compositeOperation ||
+      prevProps.pointRadiusUnit !== this.props.pointRadiusUnit ||
+      prevProps.zoom !== this.props.zoom;
+
+    if (shouldRedraw) {
+      this.redraw();
+    }
+  }
+
+  componentWillUnmount() {
+    if (this.props.mapRef?.current) {
+      this.props.mapRef.current.off('move', this.redraw);
+      this.props.mapRef.current.off('moveend', this.redraw);
+    }
+  }
+
+  updateCanvasSize() {
+    const canvas = this.canvasRef.current;
+    const container = this.containerRef.current;
+    if (!canvas || !container) return;
+
+    const { width, height } = container.getBoundingClientRect();
+    if (canvas.width !== width || canvas.height !== height) {
+      canvas.width = width;
+      canvas.height = height;
+    }
+  }
+
+  drawText(
+    ctx: CanvasRenderingContext2D,
+    pixel: [number, number],
+    options: DrawTextOptions = {},
+  ) {
+    const {
+      fontHeight = 0,
+      label = '',
+      radius = 0,
+      rgb = DEFAULT_RGB_COLOR,
+      shadow = false,
+    } = options;
+    const maxWidth = radius * TEXT_WIDTH_RATIO;
+    const luminance = luminanceFromRGB(rgb[0], rgb[1], rgb[2]);
+
+    ctx.globalCompositeOperation = 'source-over';
+    ctx.fillStyle = luminance <= LUMINANCE_THRESHOLD_DARK ? 'white' : 'black';
+    ctx.font = `${fontHeight}px sans-serif`;
+    ctx.textAlign = 'center';
+    ctx.textBaseline = 'middle';
+    if (shadow) {
+      ctx.shadowBlur = 15;
+      ctx.shadowColor = luminance <= LUMINANCE_THRESHOLD_DARK ? 'black' : '';
+    }
+
+    const textWidth = ctx.measureText(String(label)).width;
+    if (textWidth > maxWidth) {
+      const scale = fontHeight / textWidth;
+      ctx.font = `${scale * maxWidth}px sans-serif`;
+    }
+
+    const { compositeOperation } = this.props;
+
+    ctx.fillText(String(label), pixel[0], pixel[1]);
+    ctx.globalCompositeOperation = compositeOperation || 'source-over';
+    ctx.shadowBlur = 0;
+    ctx.shadowColor = '';

Review Comment:
   **Suggestion:** Using an empty string for canvas shadow colors is not a 
valid CSS color and may be treated inconsistently by browsers; use a valid 
color like 'transparent' to clear the shadow color and avoid relying on 
empty-string behavior. [possible bug]
   
   **Severity Level:** Critical 🚨
   ```suggestion
         ctx.shadowColor = luminance <= LUMINANCE_THRESHOLD_DARK ? 'black' : 
'transparent';
       }
   
       const textWidth = ctx.measureText(String(label)).width;
       if (textWidth > maxWidth) {
         const scale = fontHeight / textWidth;
         ctx.font = `${scale * maxWidth}px sans-serif`;
       }
   
       const { compositeOperation } = this.props;
   
       ctx.fillText(String(label), pixel[0], pixel[1]);
       ctx.globalCompositeOperation = compositeOperation || 'source-over';
       ctx.shadowBlur = 0;
       ctx.shadowColor = 'transparent';
   ```
   <details>
   <summary><b>Why it matters? ⭐ </b></summary>
   
   canvasRenderingContext2D.shadowColor expects a CSS color; using an empty 
string is ambiguous across engines. Replacing '' with 'transparent' (or 
resetting to a definite color) is a small, correct defensive change that avoids 
inconsistent behavior when clearing shadows.
   </details>
   <details>
   <summary><b>Prompt for AI Agent 🤖 </b></summary>
   
   ```mdx
   This is a comment left during a code review.
   
   **Path:** 
superset-frontend/plugins/legacy-plugin-chart-map-box/src/ScatterPlotGlowOverlay.tsx
   **Line:** 225:239
   **Comment:**
        *Possible Bug: Using an empty string for canvas shadow colors is not a 
valid CSS color and may be treated inconsistently by browsers; use a valid 
color like 'transparent' to clear the shadow color and avoid relying on 
empty-string behavior.
   
   Validate the correctness of the flagged issue. If correct, How can I resolve 
this? If you propose a fix, implement it and please make it concise.
   ```
   </details>



##########
superset-frontend/plugins/legacy-plugin-chart-map-box/test/MapBox.test.tsx:
##########
@@ -0,0 +1,466 @@
+/**
+ * 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 { render, waitFor } from 'spec/helpers/testing-library';
+import MapBox, {
+  Clusterer,
+  DEFAULT_MAX_ZOOM,
+  DEFAULT_POINT_RADIUS,
+} from '../src/MapBox';
+import { type Location } from '../src/ScatterPlotGlowOverlay';
+
+type BBox = [number, number, number, number];
+
+const mockMap = {
+  on: jest.fn(),
+  off: jest.fn(),
+  project: jest.fn((coords: [number, number]) => ({
+    x: coords[0],
+    y: coords[1],
+  })),
+  getCanvas: jest.fn(() => ({
+    style: {},
+  })),
+  getContainer: jest.fn(() => document.createElement('div')),
+};
+
+const MockMap = ({ children, onMove, onIdle }: any) => {
+  const { useEffect } = require('react');
+  useEffect(() => {
+    onIdle?.();
+  }, [onIdle]);
+  return (
+    <div
+      data-test="mapbox-mock"
+      onClick={() =>

Review Comment:
   **Suggestion:** The test helper `MockMap` only sets 
`data-test="mapbox-mock"` while some test queries use `getByTestId` (which 
commonly looks for `data-testid`); this mismatch can cause tests to fail 
intermittently depending on test-library configuration. Add 
`data-testid="mapbox-mock"` to the mocked element to ensure both query styles 
succeed. [possible bug]
   
   **Severity Level:** Critical 🚨
   ```suggestion
         data-testid="mapbox-mock"
   ```
   <details>
   <summary><b>Why it matters? ⭐ </b></summary>
   
   The test file calls getByTestId in multiple places while the mock only 
provides data-test.
   getByTestId looks for data-testid by default, so adding 
data-testid="mapbox-mock" makes the mock robust
   across different testing-library configurations. It's a small, safe change 
that resolves a real mismatch.
   </details>
   <details>
   <summary><b>Prompt for AI Agent 🤖 </b></summary>
   
   ```mdx
   This is a comment left during a code review.
   
   **Path:** 
superset-frontend/plugins/legacy-plugin-chart-map-box/test/MapBox.test.tsx
   **Line:** 51:51
   **Comment:**
        *Possible Bug: The test helper `MockMap` only sets 
`data-test="mapbox-mock"` while some test queries use `getByTestId` (which 
commonly looks for `data-testid`); this mismatch can cause tests to fail 
intermittently depending on test-library configuration. Add 
`data-testid="mapbox-mock"` to the mocked element to ensure both query styles 
succeed.
   
   Validate the correctness of the flagged issue. If correct, How can I resolve 
this? If you propose a fix, implement it and please make it concise.
   ```
   </details>



##########
superset-frontend/plugins/legacy-plugin-chart-map-box/src/utils/geo.ts:
##########
@@ -22,7 +22,11 @@ import roundDecimal from './roundDecimal';
 export const EARTH_CIRCUMFERENCE_KM = 40075.16;
 export const MILES_PER_KM = 1.60934;
 
-export function kmToPixels(kilometers, latitude, zoomLevel) {
+export function kmToPixels(
+  kilometers: number,
+  latitude: number,
+  zoomLevel: number,
+): number {
   // Algorithm from: http://wiki.openstreetmap.org/wiki/Zoom_levels
   const latitudeRad = latitude * (Math.PI / 180);
   // Seems like the zoomLevel is off by one

Review Comment:
   **Suggestion:** Off-by-one in the denominator exponent: the map tile math 
uses a base tile size of 256 pixels (2^8), so dividing by 2 ** (zoomLevel + 9) 
makes the resolution one power-of-two too small and returns incorrect pixel 
counts; change the +9 offset to +8 to match the standard formula. [logic error]
   
   **Severity Level:** Minor ⚠️
   



##########
superset-frontend/plugins/legacy-plugin-chart-map-box/types/external.d.ts:
##########
@@ -0,0 +1,48 @@
+/**
+ * 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.
+ */
+
+declare module '*.png' {
+  const value: string;
+  export default value;
+}
+
+declare module '*.jpg' {
+  const value: string;
+  export default value;
+}
+
+declare module '*.jpeg' {
+  const value: string;
+  export default value;
+}
+
+declare module '*.svg' {
+  const value: string;
+  export default value;

Review Comment:
   **Suggestion:** SVG imports are commonly used both as a URL string and as a 
React component (via SVGR). The current declaration only exports a default 
`string`, so importing an SVG as `ReactComponent` will produce a type error at 
compile time and break code that expects the named `ReactComponent` export. Add 
a named `ReactComponent` export with appropriate React SVG props in the `.svg` 
module declaration. [type error]
   
   **Severity Level:** Minor ⚠️
   ```suggestion
     import * as React from 'react';
     const src: string;
     export default src;
     export const ReactComponent: React.FunctionComponent<
       React.SVGProps<SVGSVGElement> & { title?: string }
     >;
   ```
   <details>
   <summary><b>Why it matters? ⭐ </b></summary>
   
   This is a real, practical TypeScript issue in projects that use SVGR: 
importing { ReactComponent as Icon } from './icon.svg' will be a type error 
with the current declaration. Adding a typed ReactComponent export fixes 
compile-time errors and aligns with common bundler setups. The proposed 
improved code is correct and safe for codebases that use SVGR.
   </details>
   <details>
   <summary><b>Prompt for AI Agent 🤖 </b></summary>
   
   ```mdx
   This is a comment left during a code review.
   
   **Path:** 
superset-frontend/plugins/legacy-plugin-chart-map-box/types/external.d.ts
   **Line:** 36:37
   **Comment:**
        *Type Error: SVG imports are commonly used both as a URL string and as 
a React component (via SVGR). The current declaration only exports a default 
`string`, so importing an SVG as `ReactComponent` will produce a type error at 
compile time and break code that expects the named `ReactComponent` export. Add 
a named `ReactComponent` export with appropriate React SVG props in the `.svg` 
module declaration.
   
   Validate the correctness of the flagged issue. If correct, How can I resolve 
this? If you propose a fix, implement it and please make it concise.
   ```
   </details>



##########
superset-frontend/plugins/legacy-plugin-chart-map-box/src/transformProps.ts:
##########
@@ -16,12 +16,80 @@
  * specific language governing permissions and limitations
  * under the License.
  */
+import { ChartProps } from '@superset-ui/core';
 import Supercluster from 'supercluster';
 import { DEFAULT_POINT_RADIUS, DEFAULT_MAX_ZOOM } from './MapBox';
 
 const NOOP = () => {};
 
-export default function transformProps(chartProps) {
+export interface FormData {
+  clusteringRadius: number;
+  globalOpacity: number;
+  mapboxColor: string;
+  mapboxStyle: string;
+  pandasAggfunc: string;
+  pointRadius: number | string;
+  pointRadiusUnit: string;
+  renderWhileDragging: boolean;
+}
+
+interface ViewportChangeParams {
+  latitude: number;
+  longitude: number;
+  zoom: number;
+}
+
+interface ClusterOptions {
+  maxZoom: number;
+  radius: number;
+  initial?: () => {
+    sum: number;
+    squaredSum: number;
+    min: number;
+    max: number;
+  };
+  map?: (prop: { metric: number }) => {
+    sum: number;
+    squaredSum: number;
+    min: number;
+    max: number;
+  };
+  reduce?: (
+    accu: {
+      sum: number;
+      squaredSum: number;
+      min: number;
+      max: number;
+    },
+    prop: {
+      sum: number;
+      squaredSum: number;
+      min: number;
+      max: number;
+    },
+  ) => void;
+}
+
+interface TransformedProps {
+  width?: number;
+  height?: number;
+  aggregatorName?: string;
+  bounds?: [[number, number], [number, number]];
+  clusterer?: Supercluster;
+  globalOpacity?: number;
+  hasCustomMetric?: boolean;
+  mapboxApiKey?: string;
+  mapStyle?: string;
+  onViewportChange?: (params: ViewportChangeParams) => void;
+  pointRadius?: number;
+  pointRadiusUnit?: string;
+  renderWhileDragging?: boolean;
+  rgb?: [number, number, number, number];
+}
+
+export default function transformProps(
+  chartProps: ChartProps<FormData>,
+): TransformedProps {
   const { width, height, formData, hooks, queriesData } = chartProps;
   const { onError = NOOP, setControlValue = NOOP } = hooks;

Review Comment:
   **Suggestion:** Unsafe access of query results: `queriesData[0].data` is 
used without checking that `queriesData` exists, that it has at least one 
element, or that `.data` is defined — this can raise a runtime TypeError when 
charts are rendered with missing/empty query results. Guard the access and 
provide sensible defaults. [null pointer]
   
   **Severity Level:** Minor ⚠️
   ```suggestion
     const queryData = queriesData?.[0]?.data ?? {};
     const { bounds, geoJSON, hasCustomMetric = false, mapboxApiKey } = 
queryData;
   ```
   <details>
   <summary><b>Why it matters? ⭐ </b></summary>
   
   The PR currently assumes queriesData[0].data is always present — that's a 
real runtime footgun.
   Guarding the access (optional chaining / fallback) prevents a TypeError when 
charts are rendered
   with missing/empty query results. The suggested change is correct and 
actionable, but note that
   downstream code still uses geoJSON and other fields, so simply defaulting to 
{} may require
   additional defensive checks elsewhere.
   </details>
   <details>
   <summary><b>Prompt for AI Agent 🤖 </b></summary>
   
   ```mdx
   This is a comment left during a code review.
   
   **Path:** 
superset-frontend/plugins/legacy-plugin-chart-map-box/src/transformProps.ts
   **Line:** 93:94
   **Comment:**
        *Null Pointer: Unsafe access of query results: `queriesData[0].data` is 
used without checking that `queriesData` exists, that it has at least one 
element, or that `.data` is defined — this can raise a runtime TypeError when 
charts are rendered with missing/empty query results. Guard the access and 
provide sensible defaults.
   
   Validate the correctness of the flagged issue. If correct, How can I resolve 
this? If you propose a fix, implement it and please make it concise.
   ```
   </details>



##########
superset-frontend/plugins/legacy-plugin-chart-map-box/src/transformProps.ts:
##########
@@ -85,12 +175,13 @@ export default function transformProps(chartProps) {
     hasCustomMetric,
     mapboxApiKey,
     mapStyle: mapboxStyle,
-    onViewportChange({ latitude, longitude, zoom }) {
+    onViewportChange({ latitude, longitude, zoom }: ViewportChangeParams) {
       setControlValue('viewport_longitude', longitude);
       setControlValue('viewport_latitude', latitude);
       setControlValue('viewport_zoom', zoom);
     },
-    pointRadius: pointRadius === 'Auto' ? DEFAULT_POINT_RADIUS : pointRadius,
+    pointRadius:
+      pointRadius === 'Auto' ? DEFAULT_POINT_RADIUS : Number(pointRadius),
     pointRadiusUnit,

Review Comment:
   **Suggestion:** `pointRadius` is coerced with `Number(pointRadius)` without 
validating the result; non-numeric strings produce NaN which may break 
consumers — coerce and validate to a finite number, falling back to 
DEFAULT_POINT_RADIUS when invalid. [possible bug]
   
   **Severity Level:** Critical 🚨
   ```suggestion
         (() => {
           const pr =
             pointRadius === 'Auto' ? DEFAULT_POINT_RADIUS : 
Number(pointRadius);
           return Number.isFinite(pr) ? pr : DEFAULT_POINT_RADIUS;
         })(),
   ```
   <details>
   <summary><b>Why it matters? ⭐ </b></summary>
   
   Coercing a non-numeric string with Number(...) yields NaN which will likely 
break consumers. The IIFE
   that falls back to DEFAULT_POINT_RADIUS when Number.isFinite is false is a 
safe, localized improvement.
   </details>
   <details>
   <summary><b>Prompt for AI Agent 🤖 </b></summary>
   
   ```mdx
   This is a comment left during a code review.
   
   **Path:** 
superset-frontend/plugins/legacy-plugin-chart-map-box/src/transformProps.ts
   **Line:** 184:185
   **Comment:**
        *Possible Bug: `pointRadius` is coerced with `Number(pointRadius)` 
without validating the result; non-numeric strings produce NaN which may break 
consumers — coerce and validate to a finite number, falling back to 
DEFAULT_POINT_RADIUS when invalid.
   
   Validate the correctness of the flagged issue. If correct, How can I resolve 
this? If you propose a fix, implement it and please make it concise.
   ```
   </details>



##########
superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/common.tsx:
##########
@@ -37,7 +37,7 @@ import {
   QueryFormData,
   SetDataMaskHook,
 } from '@superset-ui/core';
-import { Layer, PickingInfo, Color } from '@deck.gl/core';
+import { PickingInfo, Color } from '@deck.gl/core';

Review Comment:
   **Suggestion:** The code imports `PickingInfo` and `Color` as runtime values 
from '@deck.gl/core' even though they are only used as TypeScript types; if 
those symbols are not exported as runtime values this will throw at runtime. 
Use a type-only import to ensure the import is erased at runtime. [possible bug]
   
   **Severity Level:** Critical 🚨
   ```suggestion
   import type { PickingInfo, Color } from '@deck.gl/core';
   ```
   <details>
   <summary><b>Why it matters? ⭐ </b></summary>
   
   Both PickingInfo and Color are only used as types in this file. Using a 
type-only import (import type ...) prevents generating a runtime binding that 
might not exist at runtime and avoids potential import errors. This is a 
correct, minimal, and safe change.
   </details>
   <details>
   <summary><b>Prompt for AI Agent 🤖 </b></summary>
   
   ```mdx
   This is a comment left during a code review.
   
   **Path:** 
superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/common.tsx
   **Line:** 40:40
   **Comment:**
        *Possible Bug: The code imports `PickingInfo` and `Color` as runtime 
values from '@deck.gl/core' even though they are only used as TypeScript types; 
if those symbols are not exported as runtime values this will throw at runtime. 
Use a type-only import to ensure the import is erased at runtime.
   
   Validate the correctness of the flagged issue. If correct, How can I resolve 
this? If you propose a fix, implement it and please make it concise.
   ```
   </details>



##########
superset-frontend/plugins/legacy-plugin-chart-map-box/test/ScatterPlotGlowOverlay.test.tsx:
##########
@@ -0,0 +1,586 @@
+/**
+ * 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 { render } from 'spec/helpers/testing-library';
+import ScatterPlotGlowOverlay, {
+  AggregationType,
+} from '../src/ScatterPlotGlowOverlay';
+
+jest.mock('react-map-gl/mapbox', () => ({
+  useMap: () => ({
+    current: {
+      project: (coords: [number, number]) => ({
+        x: coords[0] * 10,
+        y: coords[1] * 10,
+      }),
+      on: jest.fn(),
+      off: jest.fn(),
+    },
+  }),
+}));
+
+const mockLocation = {
+  geometry: {
+    coordinates: [10, 20] as [number, number],
+    type: 'Point',
+  },
+  properties: {},
+};
+
+const mockClusterLocation = {
+  geometry: {
+    coordinates: [10, 20] as [number, number],
+    type: 'Point',
+  },
+  properties: {
+    cluster: true,
+    point_count: 10,
+    sum: 100,
+    squaredSum: 1200,
+    min: 5,
+    max: 20,
+  },
+};
+
+test('renders without crashing', () => {
+  const { container } = render(
+    <ScatterPlotGlowOverlay locations={[]} rgb={[255, 0, 0, 255]} />,
+  );
+
+  expect(container.querySelector('canvas')).toBeInTheDocument();
+});
+
+test('renders canvas element', () => {
+  const { container } = render(
+    <ScatterPlotGlowOverlay
+      locations={[mockLocation]}
+      rgb={[255, 128, 64, 255]}
+    />,
+  );
+
+  const canvas = container.querySelector('canvas');
+  expect(canvas).toBeInTheDocument();
+  expect(canvas?.tagName).toBe('CANVAS');
+});
+
+test('renders with default props', () => {
+  const { container } = render(<ScatterPlotGlowOverlay locations={[]} />);
+
+  expect(container.querySelector('canvas')).toBeInTheDocument();
+});
+
+test('renders with empty locations array', () => {
+  const { container } = render(
+    <ScatterPlotGlowOverlay locations={[]} rgb={[255, 0, 0, 255]} />,
+  );
+
+  expect(container.querySelector('canvas')).toBeInTheDocument();
+});
+
+test('renders with single location', () => {
+  const { container } = render(
+    <ScatterPlotGlowOverlay
+      locations={[mockLocation]}
+      rgb={[255, 0, 0, 255]}
+    />,
+  );
+
+  expect(container.querySelector('canvas')).toBeInTheDocument();
+});
+
+test('renders with multiple locations', () => {
+  const locations = [mockLocation, { ...mockLocation }];
+  const { container } = render(
+    <ScatterPlotGlowOverlay locations={locations} rgb={[255, 0, 0, 255]} />,
+  );
+
+  expect(container.querySelector('canvas')).toBeInTheDocument();
+});
+
+test('renders with cluster location', () => {
+  const { container } = render(
+    <ScatterPlotGlowOverlay
+      locations={[mockClusterLocation]}
+      rgb={[255, 0, 0, 255]}
+      aggregation="sum"
+    />,
+  );
+
+  expect(container.querySelector('canvas')).toBeInTheDocument();
+});
+
+test('computes cluster label as point_count when no aggregation specified', () 
=> {
+  const { container } = render(
+    <ScatterPlotGlowOverlay
+      locations={[mockClusterLocation]}
+      rgb={[255, 0, 0, 255]}
+    />,
+  );
+
+  expect(container.querySelector('canvas')).toBeInTheDocument();
+});
+
+test('computes cluster label using sum aggregation', () => {
+  const location = {
+    ...mockClusterLocation,
+    properties: {
+      ...mockClusterLocation.properties,
+      sum: 150,
+    },
+  };
+
+  const { container } = render(
+    <ScatterPlotGlowOverlay
+      locations={[location]}
+      rgb={[255, 0, 0, 255]}
+      aggregation="sum"
+    />,
+  );
+
+  expect(container.querySelector('canvas')).toBeInTheDocument();
+});
+
+test('computes cluster label using min aggregation', () => {
+  const location = {
+    ...mockClusterLocation,
+    properties: {
+      ...mockClusterLocation.properties,
+      min: 5,
+    },
+  };
+
+  const { container } = render(
+    <ScatterPlotGlowOverlay
+      locations={[location]}
+      rgb={[255, 0, 0, 255]}
+      aggregation="min"
+    />,
+  );
+
+  expect(container.querySelector('canvas')).toBeInTheDocument();
+});
+
+test('computes cluster label using max aggregation', () => {
+  const location = {
+    ...mockClusterLocation,
+    properties: {
+      ...mockClusterLocation.properties,
+      max: 20,
+    },
+  };
+
+  const { container } = render(
+    <ScatterPlotGlowOverlay
+      locations={[location]}
+      rgb={[255, 0, 0, 255]}
+      aggregation="max"
+    />,
+  );
+
+  expect(container.querySelector('canvas')).toBeInTheDocument();
+});
+
+test('computes cluster label using mean aggregation', () => {
+  const location = {
+    ...mockClusterLocation,
+    properties: {
+      cluster: true,
+      point_count: 10,
+      sum: 100,
+      squaredSum: 1200,
+    },
+  };
+
+  const { container } = render(
+    <ScatterPlotGlowOverlay
+      locations={[location]}
+      rgb={[255, 0, 0, 255]}
+      aggregation="mean"
+    />,
+  );
+
+  expect(container.querySelector('canvas')).toBeInTheDocument();
+});
+
+test('computes cluster label using variance aggregation', () => {
+  const location = {
+    ...mockClusterLocation,
+    properties: {
+      cluster: true,
+      point_count: 10,
+      sum: 100,
+      squaredSum: 1200,
+    },
+  };
+
+  const { container } = render(
+    <ScatterPlotGlowOverlay
+      locations={[location]}
+      rgb={[255, 0, 0, 255]}
+      aggregation="var"
+    />,
+  );
+
+  expect(container.querySelector('canvas')).toBeInTheDocument();
+});
+
+test('computes cluster label using stdev aggregation', () => {
+  const location = {
+    ...mockClusterLocation,
+    properties: {
+      cluster: true,
+      point_count: 10,
+      sum: 100,
+      squaredSum: 1200,
+    },
+  };
+
+  const { container } = render(
+    <ScatterPlotGlowOverlay
+      locations={[location]}
+      rgb={[255, 0, 0, 255]}
+      aggregation="stdev"
+    />,
+  );
+
+  expect(container.querySelector('canvas')).toBeInTheDocument();
+});
+
+test('uses white text color on dark background (low luminance)', () => {
+  const darkRgb: [number, number, number, number] = [255, 10, 10, 10];
+  const { container } = render(
+    <ScatterPlotGlowOverlay
+      locations={[mockClusterLocation]}
+      rgb={darkRgb}
+      aggregation="sum"
+    />,
+  );
+
+  expect(container.querySelector('canvas')).toBeInTheDocument();
+});
+
+test('uses black text color on light background (high luminance)', () => {
+  const lightRgb: [number, number, number, number] = [255, 240, 240, 240];
+  const { container } = render(
+    <ScatterPlotGlowOverlay
+      locations={[mockClusterLocation]}
+      rgb={lightRgb}
+      aggregation="sum"
+    />,
+  );
+
+  expect(container.querySelector('canvas')).toBeInTheDocument();
+});
+
+test('renders point with custom radius', () => {
+  const location = {
+    ...mockLocation,
+    properties: {
+      radius: 50,
+    },
+  };
+
+  const { container } = render(
+    <ScatterPlotGlowOverlay
+      locations={[location]}
+      rgb={[255, 0, 0, 255]}
+      dotRadius={100}
+    />,
+  );
+
+  expect(container.querySelector('canvas')).toBeInTheDocument();
+});
+
+test('renders point with metric label', () => {
+  const location = {
+    ...mockLocation,
+    properties: {
+      metric: 42.5,
+    },
+  };
+
+  const { container } = render(
+    <ScatterPlotGlowOverlay locations={[location]} rgb={[255, 0, 0, 255]} />,
+  );
+
+  expect(container.querySelector('canvas')).toBeInTheDocument();
+});
+
+test('renders point with null radius (uses default)', () => {
+  const location = {
+    ...mockLocation,
+    properties: {
+      radius: null,
+    },
+  };
+
+  const { container } = render(
+    <ScatterPlotGlowOverlay locations={[location]} rgb={[255, 0, 0, 255]} />,
+  );
+
+  expect(container.querySelector('canvas')).toBeInTheDocument();
+});
+
+test('handles kilometers pointRadiusUnit', () => {
+  const location = {
+    ...mockLocation,
+    properties: {
+      radius: 10,
+    },
+  };
+
+  const { container } = render(
+    <ScatterPlotGlowOverlay
+      locations={[location]}
+      rgb={[255, 0, 0, 255]}
+      pointRadiusUnit="Kilometers"
+      zoom={10}
+    />,
+  );
+
+  expect(container.querySelector('canvas')).toBeInTheDocument();
+});
+
+test('handles miles pointRadiusUnit', () => {
+  const location = {
+    ...mockLocation,
+    properties: {
+      radius: 10,
+    },
+  };
+
+  const { container } = render(
+    <ScatterPlotGlowOverlay
+      locations={[location]}
+      rgb={[255, 0, 0, 255]}
+      pointRadiusUnit="Miles"
+      zoom={10}
+    />,
+  );
+
+  expect(container.querySelector('canvas')).toBeInTheDocument();
+});
+
+test('uses custom lngLatAccessor', () => {
+  const customAccessor = () => [50, 60] as [number, number];
+  const { container } = render(
+    <ScatterPlotGlowOverlay
+      locations={[mockLocation]}
+      rgb={[255, 0, 0, 255]}
+      lngLatAccessor={customAccessor}
+    />,
+  );
+
+  expect(container.querySelector('canvas')).toBeInTheDocument();
+});
+
+test('handles isDragging with renderWhileDragging true', () => {
+  const { container } = render(
+    <ScatterPlotGlowOverlay
+      locations={[mockLocation]}
+      rgb={[255, 0, 0, 255]}
+      isDragging
+      renderWhileDragging
+    />,
+  );
+
+  expect(container.querySelector('canvas')).toBeInTheDocument();
+});
+
+test('handles isDragging with renderWhileDragging false', () => {
+  const { container } = render(
+    <ScatterPlotGlowOverlay
+      locations={[mockLocation]}
+      rgb={[255, 0, 0, 255]}
+      isDragging
+      renderWhileDragging={false}
+    />,
+  );
+
+  expect(container.querySelector('canvas')).toBeInTheDocument();
+});
+
+test('uses custom composite operation', () => {
+  const { container } = render(
+    <ScatterPlotGlowOverlay
+      locations={[mockLocation]}
+      rgb={[255, 0, 0, 255]}
+      compositeOperation="screen"
+    />,
+  );
+
+  expect(container.querySelector('canvas')).toBeInTheDocument();
+});
+
+test('renders with all aggregation types', () => {
+  const aggregations: AggregationType[] = [
+    'sum',
+    'min',
+    'max',
+    'mean',
+    'var',
+    'stdev',
+  ];
+
+  aggregations.forEach(aggregation => {
+    const { container } = render(
+      <ScatterPlotGlowOverlay
+        locations={[mockClusterLocation]}
+        rgb={[255, 0, 0, 255]}
+        aggregation={aggregation}
+      />,
+    );
+
+    expect(container.querySelector('canvas')).toBeInTheDocument();
+  });
+});
+
+test('formats large cluster labels with "k" suffix (>= 10000)', () => {
+  const location = {
+    ...mockClusterLocation,
+    properties: {
+      cluster: true,
+      point_count: 15000,
+      sum: 15000,
+    },
+  };
+
+  const { container } = render(
+    <ScatterPlotGlowOverlay
+      locations={[location]}
+      rgb={[255, 0, 0, 255]}
+      aggregation="sum"
+    />,
+  );
+
+  expect(container.querySelector('canvas')).toBeInTheDocument();
+});
+
+test('formats cluster labels between 1000-9999 with one decimal "k" suffix', 
() => {
+  const location = {
+    ...mockClusterLocation,
+    properties: {
+      cluster: true,
+      point_count: 2500,
+      sum: 2500,
+    },
+  };
+
+  const { container } = render(
+    <ScatterPlotGlowOverlay
+      locations={[location]}
+      rgb={[255, 0, 0, 255]}
+      aggregation="sum"
+    />,
+  );
+
+  expect(container.querySelector('canvas')).toBeInTheDocument();
+});
+
+test('does not format cluster labels below 1000', () => {
+  const location = {
+    ...mockClusterLocation,
+    properties: {
+      cluster: true,
+      point_count: 999,
+      sum: 999,
+    },
+  };
+
+  const { container } = render(
+    <ScatterPlotGlowOverlay
+      locations={[location]}
+      rgb={[255, 0, 0, 255]}
+      aggregation="sum"
+    />,
+  );
+
+  expect(container.querySelector('canvas')).toBeInTheDocument();
+});
+
+test('handles missing aggregation values gracefully', () => {
+  const location = {
+    ...mockClusterLocation,
+    properties: {
+      cluster: true,
+      point_count: 10,
+    },
+  };
+
+  const { container } = render(
+    <ScatterPlotGlowOverlay
+      locations={[location]}
+      rgb={[255, 0, 0, 255]}
+      aggregation="sum"
+    />,
+  );
+
+  expect(container.querySelector('canvas')).toBeInTheDocument();
+});
+
+test('handles non-numeric metric values', () => {
+  const location = {
+    ...mockLocation,
+    properties: {
+      metric: 'N/A',
+    },
+  };
+
+  const { container } = render(
+    <ScatterPlotGlowOverlay locations={[location]} rgb={[255, 0, 0, 255]} />,
+  );
+
+  expect(container.querySelector('canvas')).toBeInTheDocument();
+});
+
+test('overlay container has correct positioning styles', () => {
+  const { container } = render(
+    <ScatterPlotGlowOverlay
+      locations={[mockLocation]}
+      rgb={[255, 0, 0, 255]}
+    />,
+  );
+
+  const overlayDiv = container.firstChild as HTMLElement;

Review Comment:
   **Suggestion:** The test asserts styles on the overlay element but casts 
`container.firstChild` directly to `HTMLElement` without checking it exists; if 
the component failed to render the container, the test will throw a runtime 
error. First assert the node is present with `toBeInTheDocument()` and only 
then cast and assert styles. [null pointer]
   
   **Severity Level:** Minor ⚠️
   ```suggestion
     expect(container.firstChild).toBeInTheDocument();
   ```
   <details>
   <summary><b>Why it matters? ⭐ </b></summary>
   
   Good, defensive change: asserting the node exists before casting avoids a 
potential null dereference and yields a clearer test failure. The rest of the 
test suite already uses toBeInTheDocument(), so this is consistent and safe.
   </details>
   <details>
   <summary><b>Prompt for AI Agent 🤖 </b></summary>
   
   ```mdx
   This is a comment left during a code review.
   
   **Path:** 
superset-frontend/plugins/legacy-plugin-chart-map-box/test/ScatterPlotGlowOverlay.test.tsx
   **Line:** 560:560
   **Comment:**
        *Null Pointer: The test asserts styles on the overlay element but casts 
`container.firstChild` directly to `HTMLElement` without checking it exists; if 
the component failed to render the container, the test will throw a runtime 
error. First assert the node is present with `toBeInTheDocument()` and only 
then cast and assert styles.
   
   Validate the correctness of the flagged issue. If correct, How can I resolve 
this? If you propose a fix, implement it and please make it concise.
   ```
   </details>



##########
superset-frontend/plugins/legacy-plugin-chart-map-box/test/ScatterPlotGlowOverlay.test.tsx:
##########
@@ -0,0 +1,586 @@
+/**
+ * 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 { render } from 'spec/helpers/testing-library';
+import ScatterPlotGlowOverlay, {
+  AggregationType,
+} from '../src/ScatterPlotGlowOverlay';
+
+jest.mock('react-map-gl/mapbox', () => ({
+  useMap: () => ({
+    current: {
+      project: (coords: [number, number]) => ({
+        x: coords[0] * 10,
+        y: coords[1] * 10,
+      }),
+      on: jest.fn(),
+      off: jest.fn(),
+    },
+  }),
+}));
+
+const mockLocation = {
+  geometry: {
+    coordinates: [10, 20] as [number, number],
+    type: 'Point',
+  },
+  properties: {},
+};
+
+const mockClusterLocation = {
+  geometry: {
+    coordinates: [10, 20] as [number, number],
+    type: 'Point',
+  },
+  properties: {
+    cluster: true,
+    point_count: 10,
+    sum: 100,
+    squaredSum: 1200,
+    min: 5,
+    max: 20,
+  },
+};
+
+test('renders without crashing', () => {
+  const { container } = render(
+    <ScatterPlotGlowOverlay locations={[]} rgb={[255, 0, 0, 255]} />,
+  );
+
+  expect(container.querySelector('canvas')).toBeInTheDocument();
+});
+
+test('renders canvas element', () => {
+  const { container } = render(
+    <ScatterPlotGlowOverlay
+      locations={[mockLocation]}
+      rgb={[255, 128, 64, 255]}
+    />,
+  );
+
+  const canvas = container.querySelector('canvas');
+  expect(canvas).toBeInTheDocument();
+  expect(canvas?.tagName).toBe('CANVAS');
+});
+
+test('renders with default props', () => {
+  const { container } = render(<ScatterPlotGlowOverlay locations={[]} />);
+
+  expect(container.querySelector('canvas')).toBeInTheDocument();
+});
+
+test('renders with empty locations array', () => {
+  const { container } = render(
+    <ScatterPlotGlowOverlay locations={[]} rgb={[255, 0, 0, 255]} />,
+  );
+
+  expect(container.querySelector('canvas')).toBeInTheDocument();
+});
+
+test('renders with single location', () => {
+  const { container } = render(
+    <ScatterPlotGlowOverlay
+      locations={[mockLocation]}
+      rgb={[255, 0, 0, 255]}
+    />,
+  );
+
+  expect(container.querySelector('canvas')).toBeInTheDocument();
+});
+
+test('renders with multiple locations', () => {
+  const locations = [mockLocation, { ...mockLocation }];
+  const { container } = render(
+    <ScatterPlotGlowOverlay locations={locations} rgb={[255, 0, 0, 255]} />,
+  );
+
+  expect(container.querySelector('canvas')).toBeInTheDocument();
+});
+
+test('renders with cluster location', () => {
+  const { container } = render(
+    <ScatterPlotGlowOverlay
+      locations={[mockClusterLocation]}
+      rgb={[255, 0, 0, 255]}
+      aggregation="sum"
+    />,
+  );
+
+  expect(container.querySelector('canvas')).toBeInTheDocument();
+});
+
+test('computes cluster label as point_count when no aggregation specified', () 
=> {
+  const { container } = render(
+    <ScatterPlotGlowOverlay
+      locations={[mockClusterLocation]}
+      rgb={[255, 0, 0, 255]}
+    />,
+  );
+
+  expect(container.querySelector('canvas')).toBeInTheDocument();
+});
+
+test('computes cluster label using sum aggregation', () => {
+  const location = {
+    ...mockClusterLocation,
+    properties: {
+      ...mockClusterLocation.properties,
+      sum: 150,
+    },
+  };
+
+  const { container } = render(
+    <ScatterPlotGlowOverlay
+      locations={[location]}
+      rgb={[255, 0, 0, 255]}
+      aggregation="sum"
+    />,
+  );
+
+  expect(container.querySelector('canvas')).toBeInTheDocument();
+});
+
+test('computes cluster label using min aggregation', () => {
+  const location = {
+    ...mockClusterLocation,
+    properties: {
+      ...mockClusterLocation.properties,
+      min: 5,
+    },
+  };
+
+  const { container } = render(
+    <ScatterPlotGlowOverlay
+      locations={[location]}
+      rgb={[255, 0, 0, 255]}
+      aggregation="min"
+    />,
+  );
+
+  expect(container.querySelector('canvas')).toBeInTheDocument();
+});
+
+test('computes cluster label using max aggregation', () => {
+  const location = {
+    ...mockClusterLocation,
+    properties: {
+      ...mockClusterLocation.properties,
+      max: 20,
+    },
+  };
+
+  const { container } = render(
+    <ScatterPlotGlowOverlay
+      locations={[location]}
+      rgb={[255, 0, 0, 255]}
+      aggregation="max"
+    />,
+  );
+
+  expect(container.querySelector('canvas')).toBeInTheDocument();
+});
+
+test('computes cluster label using mean aggregation', () => {
+  const location = {
+    ...mockClusterLocation,
+    properties: {
+      cluster: true,
+      point_count: 10,
+      sum: 100,
+      squaredSum: 1200,
+    },
+  };
+
+  const { container } = render(
+    <ScatterPlotGlowOverlay
+      locations={[location]}
+      rgb={[255, 0, 0, 255]}
+      aggregation="mean"
+    />,
+  );
+
+  expect(container.querySelector('canvas')).toBeInTheDocument();
+});
+
+test('computes cluster label using variance aggregation', () => {
+  const location = {
+    ...mockClusterLocation,
+    properties: {
+      cluster: true,
+      point_count: 10,
+      sum: 100,
+      squaredSum: 1200,
+    },
+  };
+
+  const { container } = render(
+    <ScatterPlotGlowOverlay
+      locations={[location]}
+      rgb={[255, 0, 0, 255]}
+      aggregation="var"
+    />,
+  );
+
+  expect(container.querySelector('canvas')).toBeInTheDocument();
+});
+
+test('computes cluster label using stdev aggregation', () => {
+  const location = {
+    ...mockClusterLocation,
+    properties: {
+      cluster: true,
+      point_count: 10,
+      sum: 100,
+      squaredSum: 1200,
+    },
+  };
+
+  const { container } = render(
+    <ScatterPlotGlowOverlay
+      locations={[location]}
+      rgb={[255, 0, 0, 255]}
+      aggregation="stdev"
+    />,
+  );
+
+  expect(container.querySelector('canvas')).toBeInTheDocument();
+});
+
+test('uses white text color on dark background (low luminance)', () => {
+  const darkRgb: [number, number, number, number] = [255, 10, 10, 10];
+  const { container } = render(
+    <ScatterPlotGlowOverlay
+      locations={[mockClusterLocation]}
+      rgb={darkRgb}
+      aggregation="sum"
+    />,
+  );
+
+  expect(container.querySelector('canvas')).toBeInTheDocument();
+});
+
+test('uses black text color on light background (high luminance)', () => {
+  const lightRgb: [number, number, number, number] = [255, 240, 240, 240];
+  const { container } = render(
+    <ScatterPlotGlowOverlay
+      locations={[mockClusterLocation]}
+      rgb={lightRgb}
+      aggregation="sum"
+    />,
+  );
+
+  expect(container.querySelector('canvas')).toBeInTheDocument();
+});
+
+test('renders point with custom radius', () => {
+  const location = {
+    ...mockLocation,
+    properties: {
+      radius: 50,
+    },
+  };
+
+  const { container } = render(
+    <ScatterPlotGlowOverlay
+      locations={[location]}
+      rgb={[255, 0, 0, 255]}
+      dotRadius={100}
+    />,
+  );
+
+  expect(container.querySelector('canvas')).toBeInTheDocument();
+});
+
+test('renders point with metric label', () => {
+  const location = {
+    ...mockLocation,
+    properties: {
+      metric: 42.5,
+    },
+  };
+
+  const { container } = render(
+    <ScatterPlotGlowOverlay locations={[location]} rgb={[255, 0, 0, 255]} />,
+  );
+
+  expect(container.querySelector('canvas')).toBeInTheDocument();
+});
+
+test('renders point with null radius (uses default)', () => {
+  const location = {
+    ...mockLocation,
+    properties: {
+      radius: null,
+    },
+  };
+
+  const { container } = render(
+    <ScatterPlotGlowOverlay locations={[location]} rgb={[255, 0, 0, 255]} />,
+  );
+
+  expect(container.querySelector('canvas')).toBeInTheDocument();
+});
+
+test('handles kilometers pointRadiusUnit', () => {
+  const location = {
+    ...mockLocation,
+    properties: {
+      radius: 10,
+    },
+  };
+
+  const { container } = render(
+    <ScatterPlotGlowOverlay
+      locations={[location]}
+      rgb={[255, 0, 0, 255]}
+      pointRadiusUnit="Kilometers"
+      zoom={10}
+    />,
+  );
+
+  expect(container.querySelector('canvas')).toBeInTheDocument();
+});
+
+test('handles miles pointRadiusUnit', () => {
+  const location = {
+    ...mockLocation,
+    properties: {
+      radius: 10,
+    },
+  };
+
+  const { container } = render(
+    <ScatterPlotGlowOverlay
+      locations={[location]}
+      rgb={[255, 0, 0, 255]}
+      pointRadiusUnit="Miles"
+      zoom={10}
+    />,
+  );
+
+  expect(container.querySelector('canvas')).toBeInTheDocument();
+});
+
+test('uses custom lngLatAccessor', () => {
+  const customAccessor = () => [50, 60] as [number, number];
+  const { container } = render(
+    <ScatterPlotGlowOverlay
+      locations={[mockLocation]}
+      rgb={[255, 0, 0, 255]}
+      lngLatAccessor={customAccessor}
+    />,
+  );
+
+  expect(container.querySelector('canvas')).toBeInTheDocument();
+});
+
+test('handles isDragging with renderWhileDragging true', () => {
+  const { container } = render(
+    <ScatterPlotGlowOverlay
+      locations={[mockLocation]}
+      rgb={[255, 0, 0, 255]}
+      isDragging
+      renderWhileDragging
+    />,
+  );
+
+  expect(container.querySelector('canvas')).toBeInTheDocument();
+});
+
+test('handles isDragging with renderWhileDragging false', () => {
+  const { container } = render(
+    <ScatterPlotGlowOverlay
+      locations={[mockLocation]}
+      rgb={[255, 0, 0, 255]}
+      isDragging
+      renderWhileDragging={false}
+    />,
+  );
+
+  expect(container.querySelector('canvas')).toBeInTheDocument();
+});
+
+test('uses custom composite operation', () => {
+  const { container } = render(
+    <ScatterPlotGlowOverlay
+      locations={[mockLocation]}
+      rgb={[255, 0, 0, 255]}
+      compositeOperation="screen"
+    />,
+  );
+
+  expect(container.querySelector('canvas')).toBeInTheDocument();
+});
+
+test('renders with all aggregation types', () => {
+  const aggregations: AggregationType[] = [
+    'sum',
+    'min',
+    'max',
+    'mean',
+    'var',
+    'stdev',
+  ];
+
+  aggregations.forEach(aggregation => {
+    const { container } = render(
+      <ScatterPlotGlowOverlay
+        locations={[mockClusterLocation]}
+        rgb={[255, 0, 0, 255]}
+        aggregation={aggregation}
+      />,
+    );
+
+    expect(container.querySelector('canvas')).toBeInTheDocument();
+  });
+});
+
+test('formats large cluster labels with "k" suffix (>= 10000)', () => {
+  const location = {
+    ...mockClusterLocation,
+    properties: {
+      cluster: true,
+      point_count: 15000,
+      sum: 15000,
+    },
+  };
+
+  const { container } = render(
+    <ScatterPlotGlowOverlay
+      locations={[location]}
+      rgb={[255, 0, 0, 255]}
+      aggregation="sum"
+    />,
+  );
+
+  expect(container.querySelector('canvas')).toBeInTheDocument();
+});
+
+test('formats cluster labels between 1000-9999 with one decimal "k" suffix', 
() => {
+  const location = {
+    ...mockClusterLocation,
+    properties: {
+      cluster: true,
+      point_count: 2500,
+      sum: 2500,
+    },
+  };
+
+  const { container } = render(
+    <ScatterPlotGlowOverlay
+      locations={[location]}
+      rgb={[255, 0, 0, 255]}
+      aggregation="sum"
+    />,
+  );
+
+  expect(container.querySelector('canvas')).toBeInTheDocument();
+});
+
+test('does not format cluster labels below 1000', () => {
+  const location = {
+    ...mockClusterLocation,
+    properties: {
+      cluster: true,
+      point_count: 999,
+      sum: 999,
+    },
+  };
+
+  const { container } = render(
+    <ScatterPlotGlowOverlay
+      locations={[location]}
+      rgb={[255, 0, 0, 255]}
+      aggregation="sum"
+    />,
+  );
+
+  expect(container.querySelector('canvas')).toBeInTheDocument();
+});
+
+test('handles missing aggregation values gracefully', () => {
+  const location = {
+    ...mockClusterLocation,
+    properties: {
+      cluster: true,
+      point_count: 10,
+    },
+  };
+
+  const { container } = render(
+    <ScatterPlotGlowOverlay
+      locations={[location]}
+      rgb={[255, 0, 0, 255]}
+      aggregation="sum"
+    />,
+  );
+
+  expect(container.querySelector('canvas')).toBeInTheDocument();
+});
+
+test('handles non-numeric metric values', () => {
+  const location = {
+    ...mockLocation,
+    properties: {
+      metric: 'N/A',
+    },
+  };
+
+  const { container } = render(
+    <ScatterPlotGlowOverlay locations={[location]} rgb={[255, 0, 0, 255]} />,
+  );
+
+  expect(container.querySelector('canvas')).toBeInTheDocument();
+});
+
+test('overlay container has correct positioning styles', () => {
+  const { container } = render(
+    <ScatterPlotGlowOverlay
+      locations={[mockLocation]}
+      rgb={[255, 0, 0, 255]}
+    />,
+  );
+
+  const overlayDiv = container.firstChild as HTMLElement;
+  expect(overlayDiv).toHaveStyle({
+    position: 'absolute',
+    left: '0px',
+    top: '0px',
+    width: '100%',
+    height: '100%',
+    pointerEvents: 'none',
+  });
+});
+
+test('canvas has correct positioning styles', () => {
+  const { container } = render(
+    <ScatterPlotGlowOverlay
+      locations={[mockLocation]}
+      rgb={[255, 0, 0, 255]}
+    />,
+  );
+
+  const canvas = container.querySelector('canvas') as HTMLCanvasElement;

Review Comment:
   **Suggestion:** The test queries the canvas and immediately calls 
`toHaveStyle` after casting to `HTMLCanvasElement`, but if 
`querySelector('canvas')` returns null the assertion call will throw; ensure 
the canvas exists first by asserting `toBeInTheDocument()` on the query result, 
then retrieve and assert styles. [null pointer]
   
   **Severity Level:** Minor ⚠️
   ```suggestion
     expect(container.querySelector('canvas')).toBeInTheDocument();
   ```
   <details>
   <summary><b>Why it matters? ⭐ </b></summary>
   
   Also a sensible defensive improvement: verify the canvas is present before 
casting and checking styles to avoid throwing on null. The project already 
includes toBeInTheDocument() usage elsewhere, so this change is consistent and 
low risk.
   </details>
   <details>
   <summary><b>Prompt for AI Agent 🤖 </b></summary>
   
   ```mdx
   This is a comment left during a code review.
   
   **Path:** 
superset-frontend/plugins/legacy-plugin-chart-map-box/test/ScatterPlotGlowOverlay.test.tsx
   **Line:** 579:579
   **Comment:**
        *Null Pointer: The test queries the canvas and immediately calls 
`toHaveStyle` after casting to `HTMLCanvasElement`, but if 
`querySelector('canvas')` returns null the assertion call will throw; ensure 
the canvas exists first by asserting `toBeInTheDocument()` on the query result, 
then retrieve and assert styles.
   
   Validate the correctness of the flagged issue. If correct, How can I resolve 
this? If you propose a fix, implement it and please make it concise.
   ```
   </details>



##########
superset-frontend/plugins/legacy-plugin-chart-map-box/test/utils/geo.test.ts:
##########
@@ -0,0 +1,121 @@
+/**
+ * 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 {
+  kmToPixels,
+  EARTH_CIRCUMFERENCE_KM,
+  MILES_PER_KM,
+} from '../../src/utils/geo';
+
+test('converts kilometers to pixels at equator (0 degrees) at zoom level 0', 
() => {
+  const result = kmToPixels(100, 0, 0);
+  expect(result).toBeGreaterThan(0);
+  expect(typeof result).toBe('number');
+});
+
+test('converts kilometers to pixels at equator (0 degrees) at zoom level 10', 
() => {
+  const km = 10;
+  const result = kmToPixels(km, 0, 10);
+  expect(result).toBeGreaterThan(0);
+  expect(typeof result).toBe('number');
+});
+
+test('higher zoom levels result in more pixels per kilometer', () => {
+  const km = 10;
+  const latitude = 0;
+  const lowZoom = kmToPixels(km, latitude, 5);
+  const highZoom = kmToPixels(km, latitude, 10);
+
+  expect(highZoom).toBeGreaterThan(lowZoom);
+});
+
+test('same distance at higher latitude results in more pixels (Mercator 
distortion)', () => {
+  const km = 10;
+  const zoom = 10;
+  const equatorPixels = kmToPixels(km, 0, zoom);
+  const midLatPixels = kmToPixels(km, 45, zoom);
+
+  expect(midLatPixels).toBeGreaterThan(equatorPixels);
+});
+
+test('handles negative latitudes correctly', () => {
+  const km = 10;
+  const zoom = 10;
+  const northPixels = kmToPixels(km, 45, zoom);
+  const southPixels = kmToPixels(km, -45, zoom);
+
+  expect(northPixels).toBeCloseTo(southPixels, 2);
+});
+
+test('uses correct Earth circumference constant', () => {
+  expect(EARTH_CIRCUMFERENCE_KM).toBe(40075.16);
+});
+
+test('uses correct miles to kilometers conversion factor', () => {
+  expect(MILES_PER_KM).toBe(1.60934);
+});
+
+test('returns rounded result with 2 decimal places', () => {
+  const result = kmToPixels(10, 45, 10);
+  const decimals = (result.toString().split('.')[1] || '').length;
+  expect(decimals).toBeLessThanOrEqual(2);
+});
+
+test('handles zero kilometers', () => {
+  const result = kmToPixels(0, 0, 10);
+  expect(result).toBe(0);
+});
+
+test('handles very small distances', () => {
+  const result = kmToPixels(0.001, 0, 10);
+  expect(result).toBeGreaterThanOrEqual(0);
+  expect(typeof result).toBe('number');
+});
+
+test('handles large distances', () => {
+  const result = kmToPixels(10000, 0, 5);
+  expect(result).toBeGreaterThan(0);
+  expect(typeof result).toBe('number');
+});
+
+test('handles poles (90 degrees latitude)', () => {
+  const result = kmToPixels(10, 90, 10);
+  expect(result).toBeGreaterThanOrEqual(0);
+  expect(typeof result).toBe('number');
+});
+
+test('handles poles (-90 degrees latitude)', () => {
+  const result = kmToPixels(10, -90, 10);
+  expect(result).toBeGreaterThanOrEqual(0);
+  expect(typeof result).toBe('number');

Review Comment:
   **Suggestion:** The Mercator-based formula used by `kmToPixels` can produce 
division-by-zero (cos(±90°) == 0) resulting in Infinity or NaN; current tests 
only check `typeof` and non-negativity which permit Infinity. Replace both pole 
tests with checks that the function returns a finite number (or explicitly 
handle/clamp poles in the implementation). [logic error]
   
   **Severity Level:** Minor ⚠️
   ```suggestion
   test('handles poles (±90 degrees latitude) returns finite numbers', () => {
     const northResult = kmToPixels(10, 90, 10);
     const southResult = kmToPixels(10, -90, 10);
     expect(Number.isFinite(northResult)).toBe(true);
     expect(Number.isFinite(southResult)).toBe(true);
   ```
   <details>
   <summary><b>Why it matters? ⭐ </b></summary>
   
   The current assertions (>= 0 and typeof 'number') will pass for Infinity, 
which is not useful. Asserting Number.isFinite(...) is a stronger, meaningful 
check and will catch cases where the implementation divides by zero and returns 
Infinity/NaN. This is a valid improvement to test robustness.
   </details>
   <details>
   <summary><b>Prompt for AI Agent 🤖 </b></summary>
   
   ```mdx
   This is a comment left during a code review.
   
   **Path:** 
superset-frontend/plugins/legacy-plugin-chart-map-box/test/utils/geo.test.ts
   **Line:** 97:106
   **Comment:**
        *Logic Error: The Mercator-based formula used by `kmToPixels` can 
produce division-by-zero (cos(±90°) == 0) resulting in Infinity or NaN; current 
tests only check `typeof` and non-negativity which permit Infinity. Replace 
both pole tests with checks that the function returns a finite number (or 
explicitly handle/clamp poles in the implementation).
   
   Validate the correctness of the flagged issue. If correct, How can I resolve 
this? If you propose a fix, implement it and please make it concise.
   ```
   </details>



##########
superset-frontend/spec/__mocks__/supercluster.ts:
##########
@@ -0,0 +1,33 @@
+/**
+ * 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.
+ */
+
+// Mock for supercluster package
+export default class Supercluster {
+  load() {

Review Comment:
   **Suggestion:** The mock `load` method has no parameter signature; when the 
real code calls `load(points)` TypeScript callers will get a type/signature 
mismatch (or tooling may complain) and any passed arguments are ignored—add a 
parameter with a permissive type and an explicit return type of `this` to match 
the real API. [type error]
   
   **Severity Level:** Minor ⚠️
   ```suggestion
     load(points: any[] = []): this {
   ```
   <details>
   <summary><b>Why it matters? ⭐ </b></summary>
   
   The mock currently omits the real API signature. Adding an optional points 
parameter and an explicit `this` return type matches upstream Supercluster, 
prevents TypeScript signature mismatches, and is low-risk (the suggested 
default preserves existing tests).
   </details>
   <details>
   <summary><b>Prompt for AI Agent 🤖 </b></summary>
   
   ```mdx
   This is a comment left during a code review.
   
   **Path:** superset-frontend/spec/__mocks__/supercluster.ts
   **Line:** 22:22
   **Comment:**
        *Type Error: The mock `load` method has no parameter signature; when 
the real code calls `load(points)` TypeScript callers will get a type/signature 
mismatch (or tooling may complain) and any passed arguments are ignored—add a 
parameter with a permissive type and an explicit return type of `this` to match 
the real API.
   
   Validate the correctness of the flagged issue. If correct, How can I resolve 
this? If you propose a fix, implement it and please make it concise.
   ```
   </details>



##########
superset-frontend/spec/__mocks__/supercluster.ts:
##########
@@ -0,0 +1,33 @@
+/**
+ * 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.
+ */
+
+// Mock for supercluster package
+export default class Supercluster {
+  load() {
+    return this;
+  }
+
+  getClusters() {

Review Comment:
   **Suggestion:** The mock `getClusters` method has no parameters; callers 
that pass `bbox` and `zoom` will either trigger TypeScript signature errors or 
have their arguments ignored at runtime—add parameters with permissive types 
and an explicit return type to mirror the real API. [possible bug]
   
   **Severity Level:** Critical 🚨
   ```suggestion
     getClusters(bbox: [number, number, number, number] = [0, 0, 0, 0], zoom: 
number = 0): any[] {
   ```
   <details>
   <summary><b>Why it matters? ⭐ </b></summary>
   
   Mirroring the real API (bbox and zoom) improves type accuracy and avoids 
ignored arguments. The suggested defaults keep existing tests working while 
providing correct typings for callers.
   </details>
   <details>
   <summary><b>Prompt for AI Agent 🤖 </b></summary>
   
   ```mdx
   This is a comment left during a code review.
   
   **Path:** superset-frontend/spec/__mocks__/supercluster.ts
   **Line:** 26:26
   **Comment:**
        *Possible Bug: The mock `getClusters` method has no parameters; callers 
that pass `bbox` and `zoom` will either trigger TypeScript signature errors or 
have their arguments ignored at runtime—add parameters with permissive types 
and an explicit return type to mirror the real API.
   
   Validate the correctness of the flagged issue. If correct, How can I resolve 
this? If you propose a fix, implement it and please make it concise.
   ```
   </details>



##########
superset-frontend/plugins/legacy-plugin-chart-map-box/test/transformProps.test.ts:
##########
@@ -0,0 +1,464 @@
+/**
+ * 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 { ChartProps, DatasourceType } from '@superset-ui/core';
+import { supersetTheme } from '@apache-superset/core/ui';
+import transformProps, { FormData } from '../src/transformProps';
+
+jest.mock('supercluster');
+
+const createMockChartProps = (
+  overrides: Partial<ChartProps<FormData>> = {},
+): ChartProps<FormData> => {
+  const {
+    formData: formDataOverrides,
+    queriesData: queriesDataOverrides,
+    hooks: hooksOverrides,
+    ...restOverrides
+  } = overrides;
+  return {
+    annotationData: {},
+    initialValues: {},
+    ownState: {},
+    filterState: {},
+    behaviors: [],
+    theme: supersetTheme,
+    width: 800,
+    height: 600,
+    datasource: {
+      id: 1,
+      name: 'test_table',
+      type: DatasourceType.Table,
+      columns: [],
+      metrics: [],
+      columnFormats: {},
+      verboseMap: {},
+    },
+    rawDatasource: {},
+    rawFormData: {
+      clusteringRadius: 60,
+      globalOpacity: 1,
+      mapboxColor: 'rgb(0, 0, 0)',
+      mapboxStyle: 'mapbox://styles/mapbox/streets-v11',
+      pandasAggfunc: 'sum',
+      pointRadius: 60,
+      pointRadiusUnit: 'Pixels',
+      renderWhileDragging: true,
+    },
+    hooks: {
+      onError: jest.fn(),
+      setControlValue: jest.fn(),
+      ...hooksOverrides,
+    },
+    formData: {
+      clusteringRadius: 60,
+      globalOpacity: 1,
+      mapboxColor: 'rgb(0, 0, 0)',
+      mapboxStyle: 'mapbox://styles/mapbox/streets-v11',
+      pandasAggfunc: 'sum',
+      pointRadius: 60,
+      pointRadiusUnit: 'Pixels',
+      renderWhileDragging: true,
+      ...formDataOverrides,
+    },
+    queriesData: [
+      {
+        data: {
+          bounds: [
+            [-122.5, 37.5],
+            [-122.0, 38.0],
+          ] as [[number, number], [number, number]],
+          geoJSON: {
+            type: 'FeatureCollection',
+            features: [],
+          },
+          hasCustomMetric: false,
+          mapboxApiKey: 'test-api-key',
+          ...queriesDataOverrides?.[0]?.data,
+        },
+        colnames: [],
+        coltypes: [],
+        rowcount: 0,
+        ...queriesDataOverrides?.[0],

Review Comment:
   **Suggestion:** Logic bug: `queriesDataOverrides` only uses the first 
element (`[0]`) of any provided overrides array, silently ignoring additional 
query entries; when callers pass a full `queriesData` array it will not be 
preserved or merged correctly. Return the provided array if present (optionally 
merging defaults per entry) instead of only applying the first element. [logic 
error]
   
   **Severity Level:** Minor ⚠️
   ```suggestion
       queriesData: queriesDataOverrides?.length
         ? queriesDataOverrides
         : [
             {
               data: {
                 bounds: [
                   [-122.5, 37.5],
                   [-122.0, 38.0],
                 ] as [[number, number], [number, number]],
                 geoJSON: {
                   type: 'FeatureCollection',
                   features: [],
                 },
                 hasCustomMetric: false,
                 mapboxApiKey: 'test-api-key',
               },
               colnames: [],
               coltypes: [],
               rowcount: 0,
   ```
   <details>
   <summary><b>Why it matters? ⭐ </b></summary>
   
   This is a legitimate logic bug: the helper only merges the first element of 
a provided queriesData array (queriesDataOverrides?.[0]) and ignores any 
additional entries. The improved code returns the provided array when present 
which preserves callers' intent and prevents surprises when tests or callers 
pass multiple query objects. This directly fixes a real correctness issue in 
the factory.
   </details>
   <details>
   <summary><b>Prompt for AI Agent 🤖 </b></summary>
   
   ```mdx
   This is a comment left during a code review.
   
   **Path:** 
superset-frontend/plugins/legacy-plugin-chart-map-box/test/transformProps.test.ts
   **Line:** 80:98
   **Comment:**
        *Logic Error: Logic bug: `queriesDataOverrides` only uses the first 
element (`[0]`) of any provided overrides array, silently ignoring additional 
query entries; when callers pass a full `queriesData` array it will not be 
preserved or merged correctly. Return the provided array if present (optionally 
merging defaults per entry) instead of only applying the first element.
   
   Validate the correctness of the flagged issue. If correct, How can I resolve 
this? If you propose a fix, implement it and please make it concise.
   ```
   </details>



##########
superset-frontend/plugins/legacy-plugin-chart-map-box/types/external.d.ts:
##########
@@ -0,0 +1,48 @@
+/**
+ * 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.
+ */
+
+declare module '*.png' {
+  const value: string;

Review Comment:
   **Suggestion:** Some bundlers (or frameworks like Next.js) expose image 
imports as an object (with `src`, `width`, `height`, etc.) instead of a plain 
string; typing image modules strictly as `string` can cause type mismatches at 
compile time. Broaden the image module export type to include the common object 
shape (or `any`) so both styles are accepted. [possible bug]
   
   **Severity Level:** Critical 🚨
   ```suggestion
     const value: string | { src: string; width?: number; height?: number; 
blurDataURL?: string };
   ```
   <details>
   <summary><b>Why it matters? ⭐ </b></summary>
   
   Many frameworks/bundlers (Next.js, some webpack loaders) provide an object 
with src/width/height instead of a bare string. Widening the type to a union 
covers both behaviors and prevents spurious type errors in consumers. This 
addresses a tangible compatibility issue visible in similar projects.
   </details>
   <details>
   <summary><b>Prompt for AI Agent 🤖 </b></summary>
   
   ```mdx
   This is a comment left during a code review.
   
   **Path:** 
superset-frontend/plugins/legacy-plugin-chart-map-box/types/external.d.ts
   **Line:** 21:21
   **Comment:**
        *Possible Bug: Some bundlers (or frameworks like Next.js) expose image 
imports as an object (with `src`, `width`, `height`, etc.) instead of a plain 
string; typing image modules strictly as `string` can cause type mismatches at 
compile time. Broaden the image module export type to include the common object 
shape (or `any`) so both styles are accepted.
   
   Validate the correctness of the flagged issue. If correct, How can I resolve 
this? If you propose a fix, implement it and please make it concise.
   ```
   </details>



##########
superset-frontend/plugins/legacy-plugin-chart-map-box/test/utils/geo.test.ts:
##########
@@ -0,0 +1,121 @@
+/**
+ * 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 {
+  kmToPixels,
+  EARTH_CIRCUMFERENCE_KM,
+  MILES_PER_KM,
+} from '../../src/utils/geo';
+
+test('converts kilometers to pixels at equator (0 degrees) at zoom level 0', 
() => {
+  const result = kmToPixels(100, 0, 0);
+  expect(result).toBeGreaterThan(0);
+  expect(typeof result).toBe('number');
+});
+
+test('converts kilometers to pixels at equator (0 degrees) at zoom level 10', 
() => {
+  const km = 10;
+  const result = kmToPixels(km, 0, 10);
+  expect(result).toBeGreaterThan(0);
+  expect(typeof result).toBe('number');
+});
+
+test('higher zoom levels result in more pixels per kilometer', () => {
+  const km = 10;
+  const latitude = 0;
+  const lowZoom = kmToPixels(km, latitude, 5);
+  const highZoom = kmToPixels(km, latitude, 10);
+
+  expect(highZoom).toBeGreaterThan(lowZoom);
+});
+
+test('same distance at higher latitude results in more pixels (Mercator 
distortion)', () => {
+  const km = 10;
+  const zoom = 10;
+  const equatorPixels = kmToPixels(km, 0, zoom);
+  const midLatPixels = kmToPixels(km, 45, zoom);
+
+  expect(midLatPixels).toBeGreaterThan(equatorPixels);
+});
+
+test('handles negative latitudes correctly', () => {
+  const km = 10;
+  const zoom = 10;
+  const northPixels = kmToPixels(km, 45, zoom);
+  const southPixels = kmToPixels(km, -45, zoom);
+
+  expect(northPixels).toBeCloseTo(southPixels, 2);
+});
+
+test('uses correct Earth circumference constant', () => {
+  expect(EARTH_CIRCUMFERENCE_KM).toBe(40075.16);
+});
+
+test('uses correct miles to kilometers conversion factor', () => {
+  expect(MILES_PER_KM).toBe(1.60934);
+});
+
+test('returns rounded result with 2 decimal places', () => {
+  const result = kmToPixels(10, 45, 10);
+  const decimals = (result.toString().split('.')[1] || '').length;
+  expect(decimals).toBeLessThanOrEqual(2);

Review Comment:
   **Suggestion:** The decimal-place check uses string formatting and can be 
brittle (exponential notation or edge cases); validate rounding numerically by 
asserting that rounding to 2 decimals reproduces the value (and ensure the 
result is finite). [possible bug]
   
   **Severity Level:** Critical 🚨
   ```suggestion
     expect(Number.isFinite(result)).toBe(true);
     expect(Math.round(result * 100) / 100).toBeCloseTo(result, 10);
   ```
   <details>
   <summary><b>Why it matters? ⭐ </b></summary>
   
   Counting decimal digits by converting to string can break for numbers 
formatted in exponential notation or edge cases. Verifying numerically that 
rounding to 2 decimals reproduces the value (or that the value equals the 
rounded value within a tiny epsilon) is more robust and directly verifies the 
intended behavior of roundDecimal.
   </details>
   <details>
   <summary><b>Prompt for AI Agent 🤖 </b></summary>
   
   ```mdx
   This is a comment left during a code review.
   
   **Path:** 
superset-frontend/plugins/legacy-plugin-chart-map-box/test/utils/geo.test.ts
   **Line:** 76:77
   **Comment:**
        *Possible Bug: The decimal-place check uses string formatting and can 
be brittle (exponential notation or edge cases); validate rounding numerically 
by asserting that rounding to 2 decimals reproduces the value (and ensure the 
result is finite).
   
   Validate the correctness of the flagged issue. If correct, How can I resolve 
this? If you propose a fix, implement it and please make it concise.
   ```
   </details>



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to