You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@superset.apache.org by be...@apache.org on 2026/04/09 00:15:06 UTC

(superset) branch master updated: feat: modernize deck.gl and map plugins with MapLibre/Mapbox dual renderer (#38035)

This is an automated email from the ASF dual-hosted git repository.

beto pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/superset.git


The following commit(s) were added to refs/heads/master by this push:
     new c49641538d5 feat: modernize deck.gl and map plugins with MapLibre/Mapbox dual renderer (#38035)
c49641538d5 is described below

commit c49641538d5e388ea684d050a4ba6a6422d1d02d
Author: Birk Skyum <74...@users.noreply.github.com>
AuthorDate: Thu Apr 9 02:14:59 2026 +0200

    feat: modernize deck.gl and map plugins with MapLibre/Mapbox dual renderer (#38035)
    
    Co-authored-by: Beto Dealmeida <ro...@dealmeida.net>
---
 superset-frontend/package-lock.json                | 934 ++++++++++++++-------
 superset-frontend/package.json                     |   4 +-
 .../superset-ui-core/src/chart/types/VizType.ts    |   1 +
 .../legacy-plugin-chart-map-box/CHANGELOG.md       |  55 --
 .../plugins/legacy-plugin-chart-map-box/README.md  |  52 --
 .../legacy-plugin-chart-map-box/src/MapBox.tsx     | 243 ------
 .../src/ScatterPlotGlowOverlay.tsx                 | 425 ----------
 .../src/stories/MapBox.stories.tsx                 | 107 ---
 .../src/transformProps.ts                          | 162 ----
 .../test/MapBox.test.tsx                           | 381 ---------
 .../legacy-plugin-chart-map-box/test/tsconfig.json |   9 -
 .../test/utils/roundDecimal.test.ts                |  28 -
 .../legacy-plugin-chart-map-box/tsconfig.json      |  25 -
 .../types/external.d.ts                            | 101 ---
 .../legacy-preset-chart-deckgl/CHANGELOG.md        |  89 --
 .../plugins/legacy-preset-chart-deckgl/README.md   |  57 --
 .../test/utils/colors.test.ts                      |  25 -
 .../test/utils/getPointsFromPolygon.test.ts        |  56 --
 .../legacy-preset-chart-deckgl/types/external.d.ts |  71 --
 .../package.json                                   |  19 +-
 .../src/MapLibre.css}                              |   2 +-
 .../src/MapLibre.tsx                               | 216 +++++
 .../src/buildQuery.ts                              |  90 ++
 .../src/components/CanvasOverlay.tsx               | 121 +++
 .../src/components/ScatterPlotOverlay.tsx          | 400 +++++++++
 .../src/controlPanel.ts                            | 126 ++-
 .../src/images/MapBox-dark.jpg                     | Bin
 .../src/images/MapBox.jpg                          | Bin
 .../src/images/MapBox2-dark.jpg                    | Bin
 .../src/images/MapBox2.jpg                         | Bin
 .../src/images/thumbnail-dark.png                  | Bin
 .../src/images/thumbnail.png                       | Bin
 .../src/images/thumbnailLarge.png                  | Bin
 .../src/index.ts                                   |  11 +-
 .../src/stories/PointClusterMap.stories.tsx        | 176 ++++
 .../src/stories/data.ts                            |   0
 .../src/transformProps.ts                          | 292 +++++++
 .../src/utils/geo.ts                               |   0
 .../src/utils/luminanceFromRGB.ts                  |   0
 .../src/utils/mapbox.ts}                           |  25 +-
 .../src/utils/roundDecimal.ts                      |   0
 .../test/MapLibre.test.tsx                         | 262 ++++++
 .../test/ScatterPlotOverlay.test.tsx}              |  88 +-
 .../test/controlPanel.test.ts                      |   0
 .../test/transformProps.test.ts                    |  88 +-
 .../tsconfig.json                                  |   5 -
 .../types/external.d.ts}                           |  13 +-
 .../package.json                                   |  27 +-
 .../src/CategoricalDeckGLContainer.tsx             |  17 +-
 .../src/DeckGLContainer.tsx                        | 138 ++-
 .../src/Multi/Multi.test.tsx                       |   2 +-
 .../src/Multi/Multi.tsx                            |  14 +-
 .../src/Multi/controlPanel.test.ts                 |   0
 .../src/Multi/controlPanel.ts                      |  10 +-
 .../src/Multi/images/example-dark.png              | Bin
 .../src/Multi/images/example.png                   | Bin
 .../src/Multi/images/thumbnail-dark.png            | Bin
 .../src/Multi/images/thumbnail.png                 | Bin
 .../src/Multi/images/thumbnailLarge.png            | Bin
 .../src/Multi/index.ts                             |   0
 .../src/TooltipRow.tsx                             |   0
 .../src/components/DeckGLOverlayMapLibre.tsx}      |  14 +-
 .../src/components/DeckGLOverlayMapbox.tsx}        |  14 +-
 .../src/components/Legend.tsx                      |  10 +-
 .../src/components/Tooltip.tsx                     |   8 +-
 .../src/factory.tsx                                |  15 +-
 .../src/index.ts                                   |   0
 .../src/layers/Arc/Arc.tsx                         |   0
 .../src/layers/Arc/buildQuery.ts                   |   0
 .../src/layers/Arc/controlPanel.ts                 |   9 +-
 .../src/layers/Arc/images/example-dark.png         | Bin
 .../src/layers/Arc/images/example.png              | Bin
 .../src/layers/Arc/images/thumbnail-dark.png       | Bin
 .../src/layers/Arc/images/thumbnail.png            | Bin
 .../src/layers/Arc/images/thumbnailLarge.png       | Bin
 .../src/layers/Arc/index.ts                        |   0
 .../src/layers/Arc/stories/Arc.stories.tsx         |   7 +-
 .../src/layers/Arc/stories/payload.ts              |   0
 .../src/layers/Arc/transformProps.ts               |   0
 .../src/layers/Contour/Contour.tsx                 |   0
 .../src/layers/Contour/buildQuery.ts               |   0
 .../src/layers/Contour/controlPanel.ts             |   4 +
 .../src/layers/Contour/getSafeCellSize.test.ts     |   0
 .../src/layers/Contour/getSafeCellSize.ts          |   0
 .../src/layers/Contour/images/example-dark.png     | Bin
 .../src/layers/Contour/images/example.png          | Bin
 .../src/layers/Contour/images/thumbnail-dark.png   | Bin
 .../src/layers/Contour/images/thumbnail.png        | Bin
 .../src/layers/Contour/images/thumbnailLarge.png   | Bin
 .../src/layers/Contour/index.ts                    |   0
 .../src/layers/Contour/transformProps.ts           |   0
 .../src/layers/Geojson/Geojson.test.ts             |   5 +-
 .../src/layers/Geojson/Geojson.tsx                 |  16 +-
 .../src/layers/Geojson/buildQuery.ts               |  86 ++
 .../src/layers/Geojson/controlPanel.ts             |   9 +-
 .../src/layers/Geojson/images/example-dark.png     | Bin
 .../src/layers/Geojson/images/example.png          | Bin
 .../src/layers/Geojson/images/thumbnail-dark.png   | Bin
 .../src/layers/Geojson/images/thumbnail.png        | Bin
 .../src/layers/Geojson/images/thumbnailLarge.png   | Bin
 .../src/layers/Geojson/index.ts                    |   5 +-
 .../src/layers/Geojson/transformProps.ts           |  50 ++
 .../src/layers/Grid/Grid.tsx                       |   0
 .../src/layers/Grid/buildQuery.ts                  |   0
 .../src/layers/Grid/controlPanel.ts                |   4 +
 .../src/layers/Grid/images/example-dark.png        | Bin
 .../src/layers/Grid/images/example.png             | Bin
 .../src/layers/Grid/images/thumbnail-dark.png      | Bin
 .../src/layers/Grid/images/thumbnail.png           | Bin
 .../src/layers/Grid/images/thumbnailLarge.png      | Bin
 .../src/layers/Grid/index.ts                       |   0
 .../src/layers/Grid/stories/Grid.stories.tsx       |   7 +-
 .../src/layers/Grid/stories/payload.ts             |   0
 .../src/layers/Grid/transformProps.ts              |   0
 .../src/layers/Heatmap/Heatmap.tsx                 |   8 +-
 .../src/layers/Heatmap/buildQuery.ts               |   0
 .../src/layers/Heatmap/controlPanel.ts             |   4 +
 .../src/layers/Heatmap/images/example-dark.png     | Bin
 .../src/layers/Heatmap/images/example.png          | Bin
 .../src/layers/Heatmap/images/thumbnail-dark.png   | Bin
 .../src/layers/Heatmap/images/thumbnail.png        | Bin
 .../src/layers/Heatmap/images/thumbnailLarge.png   | Bin
 .../src/layers/Heatmap/index.ts                    |   0
 .../src/layers/Heatmap/transformProps.ts           |   0
 .../src/layers/Hex/Hex.tsx                         |   0
 .../src/layers/Hex/buildQuery.ts                   |   0
 .../src/layers/Hex/controlPanel.ts                 |   4 +
 .../src/layers/Hex/images/example-dark.png         | Bin
 .../src/layers/Hex/images/example.png              | Bin
 .../src/layers/Hex/images/thumbnail-dark.png       | Bin
 .../src/layers/Hex/images/thumbnail.png            | Bin
 .../src/layers/Hex/images/thumbnailLarge.png       | Bin
 .../src/layers/Hex/index.ts                        |   0
 .../src/layers/Hex/stories/Hex.stories.tsx         |   7 +-
 .../src/layers/Hex/stories/payload.ts              |   0
 .../src/layers/Hex/transformProps.ts               |   0
 .../src/layers/Path/Path.test.tsx                  |   0
 .../src/layers/Path/Path.tsx                       |   2 +-
 .../src/layers/Path/buildQuery.ts                  |   0
 .../src/layers/Path/controlPanel.ts                |   4 +
 .../src/layers/Path/images/example-dark.png        | Bin
 .../src/layers/Path/images/example.png             | Bin
 .../src/layers/Path/images/thumbnail-dark.png      | Bin
 .../src/layers/Path/images/thumbnail.png           | Bin
 .../src/layers/Path/images/thumbnailLarge.png      | Bin
 .../src/layers/Path/index.ts                       |   0
 .../src/layers/Path/stories/Path.stories.tsx       |   7 +-
 .../src/layers/Path/stories/payload.ts             |   0
 .../src/layers/Path/transformProps.ts              |   0
 .../src/layers/Polygon/Polygon.test.tsx            |   2 +-
 .../src/layers/Polygon/Polygon.tsx                 |   3 +-
 .../src/layers/Polygon/buildQuery.test.ts          |   0
 .../src/layers/Polygon/buildQuery.ts               |   0
 .../src/layers/Polygon/controlPanel.ts             |  10 +-
 .../src/layers/Polygon/images/example-dark.png     | Bin
 .../src/layers/Polygon/images/example.png          | Bin
 .../src/layers/Polygon/images/thumbnail-dark.png   | Bin
 .../src/layers/Polygon/images/thumbnail.png        | Bin
 .../src/layers/Polygon/images/thumbnailLarge.png   | Bin
 .../src/layers/Polygon/index.ts                    |   0
 .../src/layers/Polygon/stories/Polygon.stories.tsx |  10 +-
 .../src/layers/Polygon/stories/geojsonPayload.ts   |   0
 .../src/layers/Polygon/stories/payload.ts          |   0
 .../src/layers/Polygon/transformProps.test.ts      |   0
 .../src/layers/Polygon/transformProps.ts           |   0
 .../src/layers/Scatter/Scatter.tsx                 |   0
 .../src/layers/Scatter/buildQuery.test.ts          |   0
 .../src/layers/Scatter/buildQuery.ts               |   0
 .../src/layers/Scatter/controlPanel.ts             |   9 +-
 .../src/layers/Scatter/images/example-dark.png     | Bin
 .../src/layers/Scatter/images/example.png          | Bin
 .../src/layers/Scatter/images/thumbnail-dark.png   | Bin
 .../src/layers/Scatter/images/thumbnail.png        | Bin
 .../src/layers/Scatter/images/thumbnailLarge.png   | Bin
 .../src/layers/Scatter/index.ts                    |   0
 .../src/layers/Scatter/stories/Scatter.stories.tsx |   7 +-
 .../src/layers/Scatter/stories/payload.ts          |   0
 .../src/layers/Scatter/transformProps.test.ts      |   0
 .../src/layers/Scatter/transformProps.ts           |   0
 .../src/layers/Screengrid/Screengrid.tsx           |   9 +-
 .../src/layers/Screengrid/buildQuery.ts            |   0
 .../src/layers/Screengrid/controlPanel.ts          |   9 +-
 .../src/layers/Screengrid/images/example-dark.png  | Bin
 .../src/layers/Screengrid/images/example.png       | Bin
 .../layers/Screengrid/images/thumbnail-dark.png    | Bin
 .../src/layers/Screengrid/images/thumbnail.png     | Bin
 .../layers/Screengrid/images/thumbnailLarge.png    | Bin
 .../src/layers/Screengrid/index.ts                 |   0
 .../Screengrid/stories/Screengrid.stories.tsx      |   8 +-
 .../src/layers/Screengrid/stories/payload.ts       |   0
 .../src/layers/Screengrid/transformProps.ts        |   0
 .../src/layers/buildQueryUtils.ts                  |   0
 .../src/layers/common.test.ts                      |   0
 .../src/layers/common.tsx                          |   0
 .../src/layers/index.ts                            |   0
 .../src/layers/spatialUtils.test.ts                |   2 +-
 .../src/layers/spatialUtils.ts                     |  26 +-
 .../src/layers/transformUtils.test.ts              |   0
 .../src/layers/transformUtils.ts                   |   0
 .../src/layers/utils/metricUtils.test.ts           |   0
 .../src/layers/utils/metricUtils.ts                |   0
 .../src/preset.ts                                  |   0
 .../src/transformProps.ts                          |   0
 .../src/types.ts                                   |   0
 .../src/utilities/HandlebarsRenderer.tsx           |   0
 .../src/utilities/Shared_DeckGL.tsx                |  71 +-
 .../src/utilities/TooltipTemplateControl.tsx       |   0
 .../src/utilities/TooltipTemplateEditor.tsx        |   0
 .../src/utilities/controlRegistry.tsx              |   0
 .../src/utilities/controls.ts                      |   0
 .../src/utilities/multiValueUtils.ts               |   0
 .../src/utilities/sharedDndControls.tsx            |   0
 .../src/utilities/tooltipUtils.tsx                 |   0
 .../src/utilities/utils.ts                         |   0
 .../src/utils.test.ts                              |   0
 .../src/utils.ts                                   |  45 -
 .../src/utils/colors.ts                            |   0
 .../src/utils/computeBoundsFromPoints.ts           |   0
 .../src/utils/crossFiltersDataMask.test.ts         |   0
 .../src/utils/crossFiltersDataMask.ts              |   0
 .../src/utils/explore.ts                           |   0
 .../src/utils/fitViewport.ts                       |   0
 .../src/utils/geo.ts                               |   0
 .../src/utils/getPointsFromPolygon.ts              |   0
 .../src/utils/mapbox.ts}                           |  25 +-
 .../src/utils/safeStringify.ts                     |   0
 .../src/utils/sandbox.ts                           |   0
 .../tsconfig.json                                  |   5 -
 .../types/external.d.ts}                           |  13 +-
 superset-frontend/scripts/check-custom-rules.js    |   2 +
 .../VizTypeControl/VizTypeControl.test.tsx         |   2 +-
 .../src/visualizations/presets/MainPreset.ts       |   6 +-
 superset-frontend/tsconfig.json                    |   4 +-
 superset/config.py                                 |  18 +-
 ...00-00_ce6bd21901ab_migrate_deckgl_and_mapbox.py | 172 ++++
 superset/viz.py                                    |  23 +
 ...ce6bd21901ab_migrate_deckgl_and_mapbox__test.py | 187 +++++
 237 files changed, 3325 insertions(+), 2609 deletions(-)

diff --git a/superset-frontend/package-lock.json b/superset-frontend/package-lock.json
index d0863f12358..2c820279737 100644
--- a/superset-frontend/package-lock.json
+++ b/superset-frontend/package-lock.json
@@ -47,21 +47,21 @@
         "@superset-ui/legacy-plugin-chart-chord": "file:./plugins/legacy-plugin-chart-chord",
         "@superset-ui/legacy-plugin-chart-country-map": "file:./plugins/legacy-plugin-chart-country-map",
         "@superset-ui/legacy-plugin-chart-horizon": "file:./plugins/legacy-plugin-chart-horizon",
-        "@superset-ui/legacy-plugin-chart-map-box": "file:./plugins/legacy-plugin-chart-map-box",
         "@superset-ui/legacy-plugin-chart-paired-t-test": "file:./plugins/legacy-plugin-chart-paired-t-test",
         "@superset-ui/legacy-plugin-chart-parallel-coordinates": "file:./plugins/legacy-plugin-chart-parallel-coordinates",
         "@superset-ui/legacy-plugin-chart-partition": "file:./plugins/legacy-plugin-chart-partition",
         "@superset-ui/legacy-plugin-chart-rose": "file:./plugins/legacy-plugin-chart-rose",
         "@superset-ui/legacy-plugin-chart-world-map": "file:./plugins/legacy-plugin-chart-world-map",
-        "@superset-ui/legacy-preset-chart-deckgl": "file:./plugins/legacy-preset-chart-deckgl",
         "@superset-ui/legacy-preset-chart-nvd3": "file:./plugins/legacy-preset-chart-nvd3",
         "@superset-ui/plugin-chart-ag-grid-table": "file:./plugins/plugin-chart-ag-grid-table",
         "@superset-ui/plugin-chart-cartodiagram": "file:./plugins/plugin-chart-cartodiagram",
         "@superset-ui/plugin-chart-echarts": "file:./plugins/plugin-chart-echarts",
         "@superset-ui/plugin-chart-handlebars": "file:./plugins/plugin-chart-handlebars",
         "@superset-ui/plugin-chart-pivot-table": "file:./plugins/plugin-chart-pivot-table",
+        "@superset-ui/plugin-chart-point-cluster-map": "file:./plugins/plugin-chart-point-cluster-map",
         "@superset-ui/plugin-chart-table": "file:./plugins/plugin-chart-table",
         "@superset-ui/plugin-chart-word-cloud": "file:./plugins/plugin-chart-word-cloud",
+        "@superset-ui/preset-chart-deckgl": "file:./plugins/preset-chart-deckgl",
         "@superset-ui/switchboard": "file:./packages/superset-ui-switchboard",
         "@types/d3-format": "^3.0.1",
         "@types/d3-selection": "^3.0.11",
@@ -3020,6 +3020,22 @@
         "@luma.gl/engine": "~9.2.4"
       }
     },
+    "node_modules/@deck.gl/mapbox": {
+      "version": "9.2.11",
+      "resolved": "https://registry.npmjs.org/@deck.gl/mapbox/-/mapbox-9.2.11.tgz",
+      "integrity": "sha512-5OaFZgjyA4Vq6WjHUdcEdl0Phi8dwj8hSCErej0NetW90mctdbxwMt0gSbqcvWBowwhyj2QAhH0P2FcITjKG/A==",
+      "license": "MIT",
+      "dependencies": {
+        "@luma.gl/constants": "~9.2.6",
+        "@math.gl/web-mercator": "^4.1.0"
+      },
+      "peerDependencies": {
+        "@deck.gl/core": "~9.2.0",
+        "@luma.gl/constants": "~9.2.6",
+        "@luma.gl/core": "~9.2.6",
+        "@math.gl/web-mercator": "^4.1.0"
+      }
+    },
     "node_modules/@deck.gl/mesh-layers": {
       "version": "9.2.6",
       "resolved": "https://registry.npmjs.org/@deck.gl/mesh-layers/-/mesh-layers-9.2.6.tgz",
@@ -6964,19 +6980,6 @@
         "geojson-normalize": "geojson-normalize"
       }
     },
-    "node_modules/@mapbox/geojson-rewind": {
-      "version": "0.5.2",
-      "resolved": "https://registry.npmjs.org/@mapbox/geojson-rewind/-/geojson-rewind-0.5.2.tgz",
-      "integrity": "sha512-tJaT+RbYGJYStt7wI3cq4Nl4SXxG8W7JDG5DMJu97V25RnbNg3QtQtf+KD+VLjNpWKYsRvXDNmNrBgEETr1ifA==",
-      "license": "ISC",
-      "dependencies": {
-        "get-stream": "^6.0.1",
-        "minimist": "^1.2.6"
-      },
-      "bin": {
-        "geojson-rewind": "geojson-rewind"
-      }
-    },
     "node_modules/@mapbox/jsonlint-lines-primitives": {
       "version": "2.0.2",
       "resolved": "https://registry.npmjs.org/@mapbox/jsonlint-lines-primitives/-/jsonlint-lines-primitives-2.0.2.tgz",
@@ -7004,9 +7007,9 @@
       "license": "ISC"
     },
     "node_modules/@mapbox/tiny-sdf": {
-      "version": "2.0.6",
-      "resolved": "https://registry.npmjs.org/@mapbox/tiny-sdf/-/tiny-sdf-2.0.6.tgz",
-      "integrity": "sha512-qMqa27TLw+ZQz5Jk+RcwZGH7BQf5G/TrutJhspsca/3SHwmgKQ1iq+d3Jxz5oysPVYTGP6aXxCo5Lk9Er6YBAA==",
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/@mapbox/tiny-sdf/-/tiny-sdf-2.1.0.tgz",
+      "integrity": "sha512-uFJhNh36BR4OCuWIEiWaEix9CA2WzT6CAIcqVjWYpnx8+QDtS+oC4QehRrx5cX4mgWs37MmKnwUejeHxVymzNg==",
       "license": "BSD-2-Clause"
     },
     "node_modules/@mapbox/unitbezier": {
@@ -7033,6 +7036,118 @@
         "node": ">=6.0.0"
       }
     },
+    "node_modules/@maplibre/geojson-vt": {
+      "version": "6.0.4",
+      "resolved": "https://registry.npmjs.org/@maplibre/geojson-vt/-/geojson-vt-6.0.4.tgz",
+      "integrity": "sha512-HYv3POhMRCdhP3UPPATM/hfcy6/WuVIf5FKboH8u/ZuFMTnAIcSVlq5nfOqroLokd925w2QtE7YwquFOIacwVQ==",
+      "license": "ISC",
+      "dependencies": {
+        "kdbush": "^4.0.2"
+      }
+    },
+    "node_modules/@maplibre/maplibre-gl-style-spec": {
+      "version": "24.8.1",
+      "resolved": "https://registry.npmjs.org/@maplibre/maplibre-gl-style-spec/-/maplibre-gl-style-spec-24.8.1.tgz",
+      "integrity": "sha512-zxa92qF96ZNojLxeAjnaRpjVCy+swoUNJvDhtpC90k7u5F0TMr4GmvNqMKvYrMoPB8d7gRSXbMG1hBbmgESIsw==",
+      "license": "ISC",
+      "dependencies": {
+        "@mapbox/jsonlint-lines-primitives": "~2.0.2",
+        "@mapbox/unitbezier": "^0.0.1",
+        "json-stringify-pretty-compact": "^4.0.0",
+        "minimist": "^1.2.8",
+        "quickselect": "^3.0.0",
+        "rw": "^1.3.3",
+        "tinyqueue": "^3.0.0"
+      },
+      "bin": {
+        "gl-style-format": "dist/gl-style-format.mjs",
+        "gl-style-migrate": "dist/gl-style-migrate.mjs",
+        "gl-style-validate": "dist/gl-style-validate.mjs"
+      }
+    },
+    "node_modules/@maplibre/maplibre-gl-style-spec/node_modules/json-stringify-pretty-compact": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/json-stringify-pretty-compact/-/json-stringify-pretty-compact-4.0.0.tgz",
+      "integrity": "sha512-3CNZ2DnrpByG9Nqj6Xo8vqbjT4F6N+tb4Gb28ESAZjYZ5yqvmc56J+/kuIwkaAMOyblTQhUW7PxMkUb8Q36N3Q==",
+      "license": "MIT"
+    },
+    "node_modules/@maplibre/maplibre-gl-style-spec/node_modules/quickselect": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/quickselect/-/quickselect-3.0.0.tgz",
+      "integrity": "sha512-XdjUArbK4Bm5fLLvlm5KpTFOiOThgfWWI4axAZDWg4E/0mKdZyI9tNEfds27qCi1ze/vwTR16kvmmGhRra3c2g==",
+      "license": "ISC"
+    },
+    "node_modules/@maplibre/maplibre-gl-style-spec/node_modules/rw": {
+      "version": "1.3.3",
+      "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz",
+      "integrity": "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==",
+      "license": "BSD-3-Clause"
+    },
+    "node_modules/@maplibre/mlt": {
+      "version": "1.1.8",
+      "resolved": "https://registry.npmjs.org/@maplibre/mlt/-/mlt-1.1.8.tgz",
+      "integrity": "sha512-8vtfYGidr1rNkv5IwIoU2lfe3Oy+Wa8HluzQYcQi9cveU9K3pweAal/poQj4GJ0K/EW4bTQp2wVAs09g2yDRZg==",
+      "license": "(MIT OR Apache-2.0)",
+      "dependencies": {
+        "@mapbox/point-geometry": "^1.1.0"
+      }
+    },
+    "node_modules/@maplibre/mlt/node_modules/@mapbox/point-geometry": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/@mapbox/point-geometry/-/point-geometry-1.1.0.tgz",
+      "integrity": "sha512-YGcBz1cg4ATXDCM/71L9xveh4dynfGmcLDqufR+nQQy3fKwsAZsWd/x4621/6uJaeB9mwOHE6hPeDgXz9uViUQ==",
+      "license": "ISC"
+    },
+    "node_modules/@maplibre/vt-pbf": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/@maplibre/vt-pbf/-/vt-pbf-4.3.0.tgz",
+      "integrity": "sha512-jIvp8F5hQCcreqOOpEt42TJMUlsrEcpf/kI1T2v85YrQRV6PPXUcEXUg5karKtH6oh47XJZ4kHu56pUkOuqA7w==",
+      "license": "MIT",
+      "dependencies": {
+        "@mapbox/point-geometry": "^1.1.0",
+        "@mapbox/vector-tile": "^2.0.4",
+        "@maplibre/geojson-vt": "^5.0.4",
+        "@types/geojson": "^7946.0.16",
+        "@types/supercluster": "^7.1.3",
+        "pbf": "^4.0.1",
+        "supercluster": "^8.0.1"
+      }
+    },
+    "node_modules/@maplibre/vt-pbf/node_modules/@mapbox/point-geometry": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/@mapbox/point-geometry/-/point-geometry-1.1.0.tgz",
+      "integrity": "sha512-YGcBz1cg4ATXDCM/71L9xveh4dynfGmcLDqufR+nQQy3fKwsAZsWd/x4621/6uJaeB9mwOHE6hPeDgXz9uViUQ==",
+      "license": "ISC"
+    },
+    "node_modules/@maplibre/vt-pbf/node_modules/@mapbox/vector-tile": {
+      "version": "2.0.4",
+      "resolved": "https://registry.npmjs.org/@mapbox/vector-tile/-/vector-tile-2.0.4.tgz",
+      "integrity": "sha512-AkOLcbgGTdXScosBWwmmD7cDlvOjkg/DetGva26pIRiZPdeJYjYKarIlb4uxVzi6bwHO6EWH82eZ5Nuv4T5DUg==",
+      "license": "BSD-3-Clause",
+      "dependencies": {
+        "@mapbox/point-geometry": "~1.1.0",
+        "@types/geojson": "^7946.0.16",
+        "pbf": "^4.0.1"
+      }
+    },
+    "node_modules/@maplibre/vt-pbf/node_modules/@maplibre/geojson-vt": {
+      "version": "5.0.4",
+      "resolved": "https://registry.npmjs.org/@maplibre/geojson-vt/-/geojson-vt-5.0.4.tgz",
+      "integrity": "sha512-KGg9sma45S+stfH9vPCJk1J0lSDLWZgCT9Y8u8qWZJyjFlP8MNP1WGTxIMYJZjDvVT3PDn05kN1C95Sut1HpgQ==",
+      "license": "ISC"
+    },
+    "node_modules/@maplibre/vt-pbf/node_modules/pbf": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/pbf/-/pbf-4.0.1.tgz",
+      "integrity": "sha512-SuLdBvS42z33m8ejRbInMapQe8n0D3vN/Xd5fmWM3tufNgRQFBpaW2YVJxQZV4iPNqb0vEFvssMEo5w9c6BTIA==",
+      "license": "BSD-3-Clause",
+      "dependencies": {
+        "resolve-protobuf-schema": "^2.1.0"
+      },
+      "bin": {
+        "pbf": "bin/pbf"
+      }
+    },
     "node_modules/@math.gl/core": {
       "version": "4.1.0",
       "resolved": "https://registry.npmjs.org/@math.gl/core/-/core-4.1.0.tgz",
@@ -12834,10 +12949,6 @@
       "resolved": "plugins/legacy-plugin-chart-horizon",
       "link": true
     },
-    "node_modules/@superset-ui/legacy-plugin-chart-map-box": {
-      "resolved": "plugins/legacy-plugin-chart-map-box",
-      "link": true
-    },
     "node_modules/@superset-ui/legacy-plugin-chart-paired-t-test": {
       "resolved": "plugins/legacy-plugin-chart-paired-t-test",
       "link": true
@@ -12858,10 +12969,6 @@
       "resolved": "plugins/legacy-plugin-chart-world-map",
       "link": true
     },
-    "node_modules/@superset-ui/legacy-preset-chart-deckgl": {
-      "resolved": "plugins/legacy-preset-chart-deckgl",
-      "link": true
-    },
     "node_modules/@superset-ui/legacy-preset-chart-nvd3": {
       "resolved": "plugins/legacy-preset-chart-nvd3",
       "link": true
@@ -12886,6 +12993,10 @@
       "resolved": "plugins/plugin-chart-pivot-table",
       "link": true
     },
+    "node_modules/@superset-ui/plugin-chart-point-cluster-map": {
+      "resolved": "plugins/plugin-chart-point-cluster-map",
+      "link": true
+    },
     "node_modules/@superset-ui/plugin-chart-table": {
       "resolved": "plugins/plugin-chart-table",
       "link": true
@@ -12894,6 +13005,10 @@
       "resolved": "plugins/plugin-chart-word-cloud",
       "link": true
     },
+    "node_modules/@superset-ui/preset-chart-deckgl": {
+      "resolved": "plugins/preset-chart-deckgl",
+      "link": true
+    },
     "node_modules/@superset-ui/switchboard": {
       "resolved": "packages/superset-ui-switchboard",
       "link": true
@@ -14197,12 +14312,6 @@
         "@types/node": "*"
       }
     },
-    "node_modules/@types/hammerjs": {
-      "version": "2.0.46",
-      "resolved": "https://registry.npmjs.org/@types/hammerjs/-/hammerjs-2.0.46.tgz",
-      "integrity": "sha512-ynRvcq6wvqexJ9brDMS4BnBLzmr0e14d6ZJTEShTBWKymQiHwlAyGu0ZPEFI2Fh1U53F7tN9ufClWM5KvqkKOw==",
-      "license": "MIT"
-    },
     "node_modules/@types/hast": {
       "version": "2.3.10",
       "resolved": "https://registry.npmjs.org/@types/hast/-/hast-2.3.10.tgz",
@@ -14599,15 +14708,6 @@
         "@types/geojson": "*"
       }
     },
-    "node_modules/@types/mapbox-gl": {
-      "version": "2.7.21",
-      "resolved": "https://registry.npmjs.org/@types/mapbox-gl/-/mapbox-gl-2.7.21.tgz",
-      "integrity": "sha512-Dx9MuF2kKgT/N22LsMUB4b3acFZh9clVqz9zv1fomoiPoBrJolwYxpWA/9LPO/2N0xWbKi4V+pkjTaFkkx/4wA==",
-      "license": "MIT",
-      "dependencies": {
-        "@types/geojson": "*"
-      }
-    },
     "node_modules/@types/mdast": {
       "version": "4.0.4",
       "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz",
@@ -16171,6 +16271,72 @@
         "win32"
       ]
     },
+    "node_modules/@vis.gl/react-mapbox": {
+      "version": "8.1.0",
+      "resolved": "https://registry.npmjs.org/@vis.gl/react-mapbox/-/react-mapbox-8.1.0.tgz",
+      "integrity": "sha512-FwvH822oxEjWYOr+pP2L8hpv+7cZB2UsQbHHHT0ryrkvvqzmTgt7qHDhamv0EobKw86e1I+B4ojENdJ5G5BkyQ==",
+      "license": "MIT",
+      "peerDependencies": {
+        "mapbox-gl": ">=3.5.0",
+        "react": ">=16.3.0",
+        "react-dom": ">=16.3.0"
+      },
+      "peerDependenciesMeta": {
+        "mapbox-gl": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@vis.gl/react-maplibre": {
+      "version": "8.1.0",
+      "resolved": "https://registry.npmjs.org/@vis.gl/react-maplibre/-/react-maplibre-8.1.0.tgz",
+      "integrity": "sha512-PkAK/gp3mUfhCLhUuc+4gc3PN9zCtVGxTF2hB6R5R5yYUw+hdg84OZ770U5MU4tPMTCG6fbduExuIW6RRKN6qQ==",
+      "license": "MIT",
+      "dependencies": {
+        "@maplibre/maplibre-gl-style-spec": "^19.2.1"
+      },
+      "peerDependencies": {
+        "maplibre-gl": ">=4.0.0",
+        "react": ">=16.3.0",
+        "react-dom": ">=16.3.0"
+      },
+      "peerDependenciesMeta": {
+        "maplibre-gl": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@vis.gl/react-maplibre/node_modules/@maplibre/maplibre-gl-style-spec": {
+      "version": "19.3.3",
+      "resolved": "https://registry.npmjs.org/@maplibre/maplibre-gl-style-spec/-/maplibre-gl-style-spec-19.3.3.tgz",
+      "integrity": "sha512-cOZZOVhDSulgK0meTsTkmNXb1ahVvmTmWmfx9gRBwc6hq98wS9JP35ESIoNq3xqEan+UN+gn8187Z6E4NKhLsw==",
+      "license": "ISC",
+      "dependencies": {
+        "@mapbox/jsonlint-lines-primitives": "~2.0.2",
+        "@mapbox/unitbezier": "^0.0.1",
+        "json-stringify-pretty-compact": "^3.0.0",
+        "minimist": "^1.2.8",
+        "rw": "^1.3.3",
+        "sort-object": "^3.0.3"
+      },
+      "bin": {
+        "gl-style-format": "dist/gl-style-format.mjs",
+        "gl-style-migrate": "dist/gl-style-migrate.mjs",
+        "gl-style-validate": "dist/gl-style-validate.mjs"
+      }
+    },
+    "node_modules/@vis.gl/react-maplibre/node_modules/json-stringify-pretty-compact": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/json-stringify-pretty-compact/-/json-stringify-pretty-compact-3.0.0.tgz",
+      "integrity": "sha512-Rc2suX5meI0S3bfdZuA7JMFBGkJ875ApfVyq2WHELjBiiG22My/l7/8zPpH/CfFVQHuVLd8NLR0nv6vi0BYYKA==",
+      "license": "MIT"
+    },
+    "node_modules/@vis.gl/react-maplibre/node_modules/rw": {
+      "version": "1.3.3",
+      "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz",
+      "integrity": "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==",
+      "license": "BSD-3-Clause"
+    },
     "node_modules/@visx/annotation": {
       "version": "3.12.0",
       "resolved": "https://registry.npmjs.org/@visx/annotation/-/annotation-3.12.0.tgz",
@@ -17620,6 +17786,15 @@
         "deep-equal": "^2.0.5"
       }
     },
+    "node_modules/arr-union": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz",
+      "integrity": "sha512-sKpyeERZ02v1FeCZT8lrfJq5u6goHCtpTAzPwJYe7c8SPFOboNjNg1vz2L4VTn9T4PQxEx13TbXLmYUcS6Ug7Q==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
     "node_modules/array-buffer-byte-length": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz",
@@ -17843,6 +18018,15 @@
         "node": ">=12"
       }
     },
+    "node_modules/assign-symbols": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz",
+      "integrity": "sha512-Q+JC7Whu8HhmTdBph/Tq59IoRtoy6KAm5zzPv00WdujX82lbAL8K7WVjne7vdCsAmbF4AYaDOPyO3k0kl8qIrw==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
     "node_modules/ast-types": {
       "version": "0.16.1",
       "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.16.1.tgz",
@@ -18838,6 +19022,25 @@
         "node": ">=6.0.0"
       }
     },
+    "node_modules/bytewise": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/bytewise/-/bytewise-1.1.0.tgz",
+      "integrity": "sha512-rHuuseJ9iQ0na6UDhnrRVDh8YnWVlU6xM3VH6q/+yHDeUH2zIhUzP+2/h3LIrhLDBtTqzWpE3p3tP/boefskKQ==",
+      "license": "MIT",
+      "dependencies": {
+        "bytewise-core": "^1.2.2",
+        "typewise": "^1.0.3"
+      }
+    },
+    "node_modules/bytewise-core": {
+      "version": "1.2.3",
+      "resolved": "https://registry.npmjs.org/bytewise-core/-/bytewise-core-1.2.3.tgz",
+      "integrity": "sha512-nZD//kc78OOxeYtRlVk8/zXqTB4gf/nlguL1ggWA8FuchMyOxcyHR4QPQZMUmA7czC+YnaBrPUCubqAWe50DaA==",
+      "license": "MIT",
+      "dependencies": {
+        "typewise-core": "^1.2"
+      }
+    },
     "node_modules/cacache": {
       "version": "20.0.4",
       "resolved": "https://registry.npmjs.org/cacache/-/cacache-20.0.4.tgz",
@@ -25007,6 +25210,18 @@
       "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==",
       "license": "MIT"
     },
+    "node_modules/extend-shallow": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+      "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==",
+      "license": "MIT",
+      "dependencies": {
+        "is-extendable": "^0.1.0"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
     "node_modules/extract-zip": {
       "version": "2.0.1",
       "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz",
@@ -26807,6 +27022,7 @@
       "version": "6.0.1",
       "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz",
       "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==",
+      "dev": true,
       "license": "MIT",
       "engines": {
         "node": ">=10"
@@ -26845,6 +27061,15 @@
         "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1"
       }
     },
+    "node_modules/get-value": {
+      "version": "2.0.6",
+      "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz",
+      "integrity": "sha512-Ln0UQDlxH1BapMu3GPtf7CuYNwRZf2gwCuPqbyG6pB8WfmFpzqcy4xtAaAMUhnNqjMKTiCPZG2oMT3YSx8U2NA==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
     "node_modules/getos": {
       "version": "3.2.1",
       "resolved": "https://registry.npmjs.org/getos/-/getos-3.2.1.tgz",
@@ -27251,15 +27476,6 @@
         "yarn": ">=1.3.0"
       }
     },
-    "node_modules/hammerjs": {
-      "version": "2.0.8",
-      "resolved": "https://registry.npmjs.org/hammerjs/-/hammerjs-2.0.8.tgz",
-      "integrity": "sha512-tSQXBXS/MWQOn/RKckawJ61vvsDpCom87JgxiYdGwHdOa0ht0vzUWDlfioofFCRU0L+6NGDt6XzbgoJvZkMeRQ==",
-      "license": "MIT",
-      "engines": {
-        "node": ">=0.8.0"
-      }
-    },
     "node_modules/handle-thing": {
       "version": "2.0.1",
       "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz",
@@ -29013,6 +29229,15 @@
       "integrity": "sha512-IOQqts/aHWbiisY5DuPJQ0gcbvaLFCa7fBa9xoLfxBZvQ+ZI/Zh9xoI7Gk+G64N0FdK4AbibytHht2tWgpJWLg==",
       "license": "MIT"
     },
+    "node_modules/is-extendable": {
+      "version": "0.1.1",
+      "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
+      "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
     "node_modules/is-extglob": {
       "version": "2.1.1",
       "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
@@ -29273,7 +29498,6 @@
       "version": "2.0.4",
       "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz",
       "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==",
-      "dev": true,
       "license": "MIT",
       "dependencies": {
         "isobject": "^3.0.1"
@@ -29546,7 +29770,6 @@
       "version": "3.0.1",
       "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
       "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==",
-      "dev": true,
       "license": "MIT",
       "engines": {
         "node": ">=0.10.0"
@@ -35483,6 +35706,81 @@
       "integrity": "sha512-XdjUArbK4Bm5fLLvlm5KpTFOiOThgfWWI4axAZDWg4E/0mKdZyI9tNEfds27qCi1ze/vwTR16kvmmGhRra3c2g==",
       "license": "ISC"
     },
+    "node_modules/maplibre-gl": {
+      "version": "5.22.0",
+      "resolved": "https://registry.npmjs.org/maplibre-gl/-/maplibre-gl-5.22.0.tgz",
+      "integrity": "sha512-nc8YA+YSEioMZg5W0cb6Cf3wQ8aJge66dsttyBgpOArOnlmFJO1Kc5G32kYVPeUYhLpBja83T99uanmJvYAIyQ==",
+      "license": "BSD-3-Clause",
+      "dependencies": {
+        "@mapbox/jsonlint-lines-primitives": "^2.0.2",
+        "@mapbox/point-geometry": "^1.1.0",
+        "@mapbox/tiny-sdf": "^2.0.7",
+        "@mapbox/unitbezier": "^0.0.1",
+        "@mapbox/vector-tile": "^2.0.4",
+        "@mapbox/whoots-js": "^3.1.0",
+        "@maplibre/geojson-vt": "^6.0.4",
+        "@maplibre/maplibre-gl-style-spec": "^24.8.1",
+        "@maplibre/mlt": "^1.1.8",
+        "@maplibre/vt-pbf": "^4.3.0",
+        "@types/geojson": "^7946.0.16",
+        "earcut": "^3.0.2",
+        "gl-matrix": "^3.4.4",
+        "kdbush": "^4.0.2",
+        "murmurhash-js": "^1.0.0",
+        "pbf": "^4.0.1",
+        "potpack": "^2.1.0",
+        "quickselect": "^3.0.0",
+        "tinyqueue": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=16.14.0",
+        "npm": ">=8.1.0"
+      },
+      "funding": {
+        "url": "https://github.com/maplibre/maplibre-gl-js?sponsor=1"
+      }
+    },
+    "node_modules/maplibre-gl/node_modules/@mapbox/point-geometry": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/@mapbox/point-geometry/-/point-geometry-1.1.0.tgz",
+      "integrity": "sha512-YGcBz1cg4ATXDCM/71L9xveh4dynfGmcLDqufR+nQQy3fKwsAZsWd/x4621/6uJaeB9mwOHE6hPeDgXz9uViUQ==",
+      "license": "ISC"
+    },
+    "node_modules/maplibre-gl/node_modules/@mapbox/vector-tile": {
+      "version": "2.0.4",
+      "resolved": "https://registry.npmjs.org/@mapbox/vector-tile/-/vector-tile-2.0.4.tgz",
+      "integrity": "sha512-AkOLcbgGTdXScosBWwmmD7cDlvOjkg/DetGva26pIRiZPdeJYjYKarIlb4uxVzi6bwHO6EWH82eZ5Nuv4T5DUg==",
+      "license": "BSD-3-Clause",
+      "dependencies": {
+        "@mapbox/point-geometry": "~1.1.0",
+        "@types/geojson": "^7946.0.16",
+        "pbf": "^4.0.1"
+      }
+    },
+    "node_modules/maplibre-gl/node_modules/earcut": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/earcut/-/earcut-3.0.2.tgz",
+      "integrity": "sha512-X7hshQbLyMJ/3RPhyObLARM2sNxxmRALLKx1+NVFFnQ9gKzmCrxm9+uLIAdBcvc8FNLpctqlQ2V6AE92Ol9UDQ==",
+      "license": "ISC"
+    },
+    "node_modules/maplibre-gl/node_modules/pbf": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/pbf/-/pbf-4.0.1.tgz",
+      "integrity": "sha512-SuLdBvS42z33m8ejRbInMapQe8n0D3vN/Xd5fmWM3tufNgRQFBpaW2YVJxQZV4iPNqb0vEFvssMEo5w9c6BTIA==",
+      "license": "BSD-3-Clause",
+      "dependencies": {
+        "resolve-protobuf-schema": "^2.1.0"
+      },
+      "bin": {
+        "pbf": "bin/pbf"
+      }
+    },
+    "node_modules/maplibre-gl/node_modules/quickselect": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/quickselect/-/quickselect-3.0.0.tgz",
+      "integrity": "sha512-XdjUArbK4Bm5fLLvlm5KpTFOiOThgfWWI4axAZDWg4E/0mKdZyI9tNEfds27qCi1ze/vwTR16kvmmGhRra3c2g==",
+      "license": "ISC"
+    },
     "node_modules/markdown-table": {
       "version": "3.0.4",
       "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-3.0.4.tgz",
@@ -39796,9 +40094,9 @@
       }
     },
     "node_modules/potpack": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/potpack/-/potpack-2.0.0.tgz",
-      "integrity": "sha512-Q+/tYsFU9r7xoOJ+y/ZTtdVQwTWfzjbiXBDMM/JKUux3+QPP02iUuIoeBQ+Ot6oEDlC+/PGjB/5A3K7KKb7hcw==",
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/potpack/-/potpack-2.1.0.tgz",
+      "integrity": "sha512-pcaShQc1Shq0y+E7GqJqvZj8DTthWV1KeHGdi0Z6IAin2Oi3JnLCOfwnCo84qc+HAp52wT9nK9H7FAJp5a44GQ==",
       "license": "ISC"
     },
     "node_modules/powershell-utils": {
@@ -40329,12 +40627,6 @@
         "node": ">=8"
       }
     },
-    "node_modules/quickselect": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/quickselect/-/quickselect-2.0.0.tgz",
-      "integrity": "sha512-RKJ22hX8mHe3Y6wH/N3wCM6BWtjaxIyyUIkpHOvfFnxdI4yD4tBXEBKSbriGujF6jnSVkJrffuo6vxACiSSxIw==",
-      "license": "ISC"
-    },
     "node_modules/quote-stream": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/quote-stream/-/quote-stream-1.0.2.tgz",
@@ -41495,97 +41787,6 @@
         "react": "*"
       }
     },
-    "node_modules/react-map-gl": {
-      "version": "6.1.21",
-      "resolved": "https://registry.npmjs.org/react-map-gl/-/react-map-gl-6.1.21.tgz",
-      "integrity": "sha512-7ENXxAeYaI4dhol5bir3iK6TeR9teA3MF0WH6VIhmkMRdYY3lgA4t1GzDh2BwpSO340Ngw+5mC0nTsc6gd1O4w==",
-      "license": "MIT",
-      "dependencies": {
-        "@babel/runtime": "^7.0.0",
-        "@types/geojson": "^7946.0.7",
-        "@types/mapbox-gl": "^2.0.3",
-        "mapbox-gl": "^2.3.0",
-        "mjolnir.js": "^2.5.0",
-        "prop-types": "^15.7.2",
-        "resize-observer-polyfill": "^1.5.1",
-        "viewport-mercator-project": "^7.0.4"
-      },
-      "engines": {
-        "node": ">= 4",
-        "npm": ">= 3"
-      },
-      "peerDependencies": {
-        "react": ">=16.3.0"
-      }
-    },
-    "node_modules/react-map-gl/node_modules/@mapbox/mapbox-gl-supported": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/@mapbox/mapbox-gl-supported/-/mapbox-gl-supported-2.0.1.tgz",
-      "integrity": "sha512-HP6XvfNIzfoMVfyGjBckjiAOQK9WfX0ywdLubuPMPv+Vqf5fj0uCbgBQYpiqcWZT6cbyyRnTSXDheT1ugvF6UQ==",
-      "license": "BSD-3-Clause"
-    },
-    "node_modules/react-map-gl/node_modules/geojson-vt": {
-      "version": "3.2.1",
-      "resolved": "https://registry.npmjs.org/geojson-vt/-/geojson-vt-3.2.1.tgz",
-      "integrity": "sha512-EvGQQi/zPrDA6zr6BnJD/YhwAkBP8nnJ9emh3EnHQKVMfg/MRVtPbMYdgVy/IaEmn4UfagD2a6fafPDL5hbtwg==",
-      "license": "ISC"
-    },
-    "node_modules/react-map-gl/node_modules/mapbox-gl": {
-      "version": "2.15.0",
-      "resolved": "https://registry.npmjs.org/mapbox-gl/-/mapbox-gl-2.15.0.tgz",
-      "integrity": "sha512-fjv+aYrd5TIHiL7wRa+W7KjtUqKWziJMZUkK5hm8TvJ3OLeNPx4NmW/DgfYhd/jHej8wWL+QJBDbdMMAKvNC0A==",
-      "license": "SEE LICENSE IN LICENSE.txt",
-      "dependencies": {
-        "@mapbox/geojson-rewind": "^0.5.2",
-        "@mapbox/jsonlint-lines-primitives": "^2.0.2",
-        "@mapbox/mapbox-gl-supported": "^2.0.1",
-        "@mapbox/point-geometry": "^0.1.0",
-        "@mapbox/tiny-sdf": "^2.0.6",
-        "@mapbox/unitbezier": "^0.0.1",
-        "@mapbox/vector-tile": "^1.3.1",
-        "@mapbox/whoots-js": "^3.1.0",
-        "csscolorparser": "~1.0.3",
-        "earcut": "^2.2.4",
-        "geojson-vt": "^3.2.1",
-        "gl-matrix": "^3.4.3",
-        "grid-index": "^1.1.0",
-        "kdbush": "^4.0.1",
-        "murmurhash-js": "^1.0.0",
-        "pbf": "^3.2.1",
-        "potpack": "^2.0.0",
-        "quickselect": "^2.0.0",
-        "rw": "^1.3.3",
-        "supercluster": "^8.0.0",
-        "tinyqueue": "^2.0.3",
-        "vt-pbf": "^3.1.3"
-      }
-    },
-    "node_modules/react-map-gl/node_modules/mjolnir.js": {
-      "version": "2.7.3",
-      "resolved": "https://registry.npmjs.org/mjolnir.js/-/mjolnir.js-2.7.3.tgz",
-      "integrity": "sha512-Z5z/+FzZqOSO3juSVKV3zcm4R2eAlWwlKMcqHmyFEJAaLILNcDKnIbnb4/kbcGyIuhtdWrzu8WOIR7uM6I34aw==",
-      "license": "MIT",
-      "dependencies": {
-        "@types/hammerjs": "^2.0.41",
-        "hammerjs": "^2.0.8"
-      },
-      "engines": {
-        "node": ">= 4",
-        "npm": ">= 3"
-      }
-    },
-    "node_modules/react-map-gl/node_modules/rw": {
-      "version": "1.3.3",
-      "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz",
-      "integrity": "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==",
-      "license": "BSD-3-Clause"
-    },
-    "node_modules/react-map-gl/node_modules/tinyqueue": {
-      "version": "2.0.3",
-      "resolved": "https://registry.npmjs.org/tinyqueue/-/tinyqueue-2.0.3.tgz",
-      "integrity": "sha512-ppJZNDuKGgxzkHihX8v9v9G5f+18gzaTfrukGrq6ueg0lmH4nqVnA2IPG0AEH3jKEk2GRJCUhDoqpoiw3PHLBA==",
-      "license": "ISC"
-    },
     "node_modules/react-markdown": {
       "version": "8.0.7",
       "resolved": "https://registry.npmjs.org/react-markdown/-/react-markdown-8.0.7.tgz",
@@ -44089,6 +44290,21 @@
         "node": ">= 0.4"
       }
     },
+    "node_modules/set-value": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz",
+      "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==",
+      "license": "MIT",
+      "dependencies": {
+        "extend-shallow": "^2.0.1",
+        "is-extendable": "^0.1.1",
+        "is-plain-object": "^2.0.3",
+        "split-string": "^3.0.1"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
     "node_modules/setimmediate": {
       "version": "1.0.5",
       "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz",
@@ -44471,6 +44687,24 @@
         "node": ">= 14"
       }
     },
+    "node_modules/sort-asc": {
+      "version": "0.2.0",
+      "resolved": "https://registry.npmjs.org/sort-asc/-/sort-asc-0.2.0.tgz",
+      "integrity": "sha512-umMGhjPeHAI6YjABoSTrFp2zaBtXBej1a0yKkuMUyjjqu6FJsTF+JYwCswWDg+zJfk/5npWUUbd33HH/WLzpaA==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/sort-desc": {
+      "version": "0.2.0",
+      "resolved": "https://registry.npmjs.org/sort-desc/-/sort-desc-0.2.0.tgz",
+      "integrity": "sha512-NqZqyvL4VPW+RAxxXnB8gvE1kyikh8+pR+T+CXLksVRN9eiQqkQlPwqWYU0mF9Jm7UnctShlxLyAt1CaBOTL1w==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
     "node_modules/sort-keys": {
       "version": "6.0.0",
       "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-6.0.0.tgz",
@@ -44498,6 +44732,23 @@
         "url": "https://github.com/sponsors/sindresorhus"
       }
     },
+    "node_modules/sort-object": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmjs.org/sort-object/-/sort-object-3.0.3.tgz",
+      "integrity": "sha512-nK7WOY8jik6zaG9CRwZTaD5O7ETWDLZYMM12pqY8htll+7dYeqGfEUPcUBHOpSJg2vJOrvFIY2Dl5cX2ih1hAQ==",
+      "license": "MIT",
+      "dependencies": {
+        "bytewise": "^1.1.0",
+        "get-value": "^2.0.2",
+        "is-extendable": "^0.1.1",
+        "sort-asc": "^0.2.0",
+        "sort-desc": "^0.2.0",
+        "union-value": "^1.0.1"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
     "node_modules/sort-object-keys": {
       "version": "2.1.0",
       "resolved": "https://registry.npmjs.org/sort-object-keys/-/sort-object-keys-2.1.0.tgz",
@@ -44836,6 +45087,43 @@
         "url": "https://github.com/sponsors/sindresorhus"
       }
     },
+    "node_modules/split-string": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz",
+      "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==",
+      "license": "MIT",
+      "dependencies": {
+        "extend-shallow": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/split-string/node_modules/extend-shallow": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz",
+      "integrity": "sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==",
+      "license": "MIT",
+      "dependencies": {
+        "assign-symbols": "^1.0.0",
+        "is-extendable": "^1.0.1"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/split-string/node_modules/is-extendable": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz",
+      "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==",
+      "license": "MIT",
+      "dependencies": {
+        "is-plain-object": "^2.0.4"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
     "node_modules/split.js": {
       "version": "1.6.5",
       "resolved": "https://registry.npmjs.org/split.js/-/split.js-1.6.5.tgz",
@@ -47462,6 +47750,21 @@
       "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==",
       "license": "MIT"
     },
+    "node_modules/typewise": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/typewise/-/typewise-1.0.3.tgz",
+      "integrity": "sha512-aXofE06xGhaQSPzt8hlTY+/YWQhm9P0jYUp1f2XtmW/3Bk0qzXcyFWAtPoo2uTGQj1ZwbDuSyuxicq+aDo8lCQ==",
+      "license": "MIT",
+      "dependencies": {
+        "typewise-core": "^1.2.0"
+      }
+    },
+    "node_modules/typewise-core": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/typewise-core/-/typewise-core-1.2.0.tgz",
+      "integrity": "sha512-2SCC/WLzj2SbUwzFOzqMCkz5amXLlxtJqDKTICqg30x+2DZxcfZN2MvQZmGfXWKNWaKK9pBPsvkcwv8bF/gxKg==",
+      "license": "MIT"
+    },
     "node_modules/uglify-js": {
       "version": "3.19.3",
       "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz",
@@ -47642,6 +47945,21 @@
         "node": ">= 0.8.0"
       }
     },
+    "node_modules/union-value": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz",
+      "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==",
+      "license": "MIT",
+      "dependencies": {
+        "arr-union": "^3.1.0",
+        "get-value": "^2.0.6",
+        "is-extendable": "^0.1.1",
+        "set-value": "^2.0.1"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
     "node_modules/unique-string": {
       "version": "3.0.0",
       "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-3.0.0.tgz",
@@ -48383,26 +48701,6 @@
         "url": "https://opencollective.com/unified"
       }
     },
-    "node_modules/viewport-mercator-project": {
-      "version": "7.0.4",
-      "resolved": "https://registry.npmjs.org/viewport-mercator-project/-/viewport-mercator-project-7.0.4.tgz",
-      "integrity": "sha512-0jzpL6pIMocCKWg1C3mqi/N4UPgZC3FzwghEm1H+XsUo8hNZAyJc3QR7YqC816ibOR8aWT5pCsV+gCu8/BMJgg==",
-      "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.",
-      "license": "MIT",
-      "dependencies": {
-        "@math.gl/web-mercator": "^3.5.5"
-      }
-    },
-    "node_modules/viewport-mercator-project/node_modules/@math.gl/web-mercator": {
-      "version": "3.6.3",
-      "resolved": "https://registry.npmjs.org/@math.gl/web-mercator/-/web-mercator-3.6.3.tgz",
-      "integrity": "sha512-UVrkSOs02YLehKaehrxhAejYMurehIHPfFQvPFZmdJHglHOU4V2cCUApTVEwOksvCp161ypEqVp+9H6mGhTTcw==",
-      "license": "MIT",
-      "dependencies": {
-        "@babel/runtime": "^7.12.0",
-        "gl-matrix": "^3.4.0"
-      }
-    },
     "node_modules/vinyl": {
       "version": "3.0.0",
       "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-3.0.0.tgz",
@@ -48452,17 +48750,6 @@
       "dev": true,
       "license": "MIT"
     },
-    "node_modules/vt-pbf": {
-      "version": "3.1.3",
-      "resolved": "https://registry.npmjs.org/vt-pbf/-/vt-pbf-3.1.3.tgz",
-      "integrity": "sha512-2LzDFzt0mZKZ9IpVF2r69G9bXaP2Q2sArJCmcCgvfTdCCZzSyz4aCLoQyUilu37Ll56tCblIZrXFIjNUpGIlmA==",
-      "license": "MIT",
-      "dependencies": {
-        "@mapbox/point-geometry": "0.1.0",
-        "@mapbox/vector-tile": "^1.3.1",
-        "pbf": "^3.2.1"
-      }
-    },
     "node_modules/w3c-xmlserializer": {
       "version": "5.0.0",
       "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz",
@@ -52035,6 +52322,7 @@
     "plugins/legacy-plugin-chart-map-box": {
       "name": "@superset-ui/legacy-plugin-chart-map-box",
       "version": "0.20.3",
+      "extraneous": true,
       "license": "Apache-2.0",
       "dependencies": {
         "@math.gl/web-mercator": "^4.1.0",
@@ -52150,6 +52438,7 @@
     "plugins/legacy-preset-chart-deckgl": {
       "name": "@superset-ui/legacy-preset-chart-deckgl",
       "version": "0.20.4",
+      "extraneous": true,
       "license": "Apache-2.0",
       "dependencies": {
         "@deck.gl/aggregation-layers": "~9.2.11",
@@ -52199,114 +52488,6 @@
         "react-map-gl": "^6.1.19"
       }
     },
-    "plugins/legacy-preset-chart-deckgl/node_modules/@deck.gl/aggregation-layers": {
-      "version": "9.2.11",
-      "resolved": "https://registry.npmjs.org/@deck.gl/aggregation-layers/-/aggregation-layers-9.2.11.tgz",
-      "integrity": "sha512-MRFbBHtMcDkOthxXnMPm6nF08DjFDACaIQsJSyHkdWtLUTSLHsWnOTn/8QbB4ka86WyNyfJy3dibLu/m3ei2ow==",
-      "license": "MIT",
-      "dependencies": {
-        "@luma.gl/constants": "~9.2.6",
-        "@luma.gl/shadertools": "~9.2.6",
-        "@math.gl/core": "^4.1.0",
-        "@math.gl/web-mercator": "^4.1.0",
-        "d3-hexbin": "^0.2.1"
-      },
-      "peerDependencies": {
-        "@deck.gl/core": "~9.2.0",
-        "@deck.gl/layers": "~9.2.0",
-        "@luma.gl/core": "~9.2.6",
-        "@luma.gl/engine": "~9.2.6"
-      }
-    },
-    "plugins/legacy-preset-chart-deckgl/node_modules/@deck.gl/extensions": {
-      "version": "9.2.11",
-      "resolved": "https://registry.npmjs.org/@deck.gl/extensions/-/extensions-9.2.11.tgz",
-      "integrity": "sha512-zlpM4Bg1ifBziW1Juiii9NY5gyW2rEhyVTWnhagH/bpTCZ2E73OhnToYt1ouqmoxL6lMtIjhRXz6LPb7tJbHHQ==",
-      "license": "MIT",
-      "dependencies": {
-        "@luma.gl/constants": "~9.2.6",
-        "@luma.gl/shadertools": "~9.2.6",
-        "@math.gl/core": "^4.1.0"
-      },
-      "peerDependencies": {
-        "@deck.gl/core": "~9.2.0",
-        "@luma.gl/core": "~9.2.6",
-        "@luma.gl/engine": "~9.2.6"
-      }
-    },
-    "plugins/legacy-preset-chart-deckgl/node_modules/@deck.gl/mesh-layers": {
-      "version": "9.2.5",
-      "resolved": "https://registry.npmjs.org/@deck.gl/mesh-layers/-/mesh-layers-9.2.5.tgz",
-      "integrity": "sha512-OJ7Nx6xhp7+bQ4S4asUUp7PdGwcfmQpQhe5SHDGy1UBZ0yZE+ojOo9uvVfXvGBnqq4Zpg9avV+WRN1/BffBsOw==",
-      "license": "MIT",
-      "dependencies": {
-        "@loaders.gl/gltf": "^4.2.0",
-        "@loaders.gl/schema": "^4.2.0",
-        "@luma.gl/gltf": "^9.2.4",
-        "@luma.gl/shadertools": "^9.2.4"
-      },
-      "peerDependencies": {
-        "@deck.gl/core": "~9.2.0",
-        "@luma.gl/core": "~9.2.4",
-        "@luma.gl/engine": "~9.2.4",
-        "@luma.gl/gltf": "~9.2.4",
-        "@luma.gl/shadertools": "~9.2.4"
-      }
-    },
-    "plugins/legacy-preset-chart-deckgl/node_modules/@deck.gl/react": {
-      "version": "9.2.11",
-      "resolved": "https://registry.npmjs.org/@deck.gl/react/-/react-9.2.11.tgz",
-      "integrity": "sha512-7xrXlM++3A7cKZdDKSW2/EPT6sc0ob7J24sTPaHe3YlIIFvCZTkQ1U1rAT9cN2gjhkI6XsE+TugUsqhx4TGwHQ==",
-      "license": "MIT",
-      "peerDependencies": {
-        "@deck.gl/core": "~9.2.0",
-        "@deck.gl/widgets": "~9.2.0",
-        "react": ">=16.3.0",
-        "react-dom": ">=16.3.0"
-      }
-    },
-    "plugins/legacy-preset-chart-deckgl/node_modules/@luma.gl/gltf": {
-      "version": "9.2.4",
-      "resolved": "https://registry.npmjs.org/@luma.gl/gltf/-/gltf-9.2.4.tgz",
-      "integrity": "sha512-OhT9Aq8/MC9r/ii8hAW7sPlIbol1ZG74jsZXsFsoGzw9gfuxT5LX71rQ/oKnsDz7dGf2cM+bM3qe8C7QiIaUvw==",
-      "license": "MIT",
-      "dependencies": {
-        "@loaders.gl/core": "^4.2.0",
-        "@loaders.gl/gltf": "^4.2.0",
-        "@loaders.gl/textures": "^4.2.0",
-        "@math.gl/core": "^4.1.0"
-      },
-      "peerDependencies": {
-        "@luma.gl/constants": "~9.2.0",
-        "@luma.gl/core": "~9.2.0",
-        "@luma.gl/engine": "~9.2.0",
-        "@luma.gl/shadertools": "~9.2.0"
-      }
-    },
-    "plugins/legacy-preset-chart-deckgl/node_modules/@mapbox/tiny-sdf": {
-      "version": "2.0.7",
-      "resolved": "https://registry.npmjs.org/@mapbox/tiny-sdf/-/tiny-sdf-2.0.7.tgz",
-      "integrity": "sha512-25gQLQMcpivjOSA40g3gO6qgiFPDpWRoMfd+G/GoppPIeP6JDaMMkMrEJnMZhKyyS6iKwVt5YKu02vCUyJM3Ug==",
-      "license": "BSD-2-Clause"
-    },
-    "plugins/legacy-preset-chart-deckgl/node_modules/d3-array": {
-      "version": "3.2.4",
-      "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz",
-      "integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==",
-      "license": "ISC",
-      "dependencies": {
-        "internmap": "1 - 2"
-      },
-      "engines": {
-        "node": ">=12"
-      }
-    },
-    "plugins/legacy-preset-chart-deckgl/node_modules/internmap": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/internmap/-/internmap-1.0.1.tgz",
-      "integrity": "sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw==",
-      "license": "ISC"
-    },
     "plugins/legacy-preset-chart-nvd3": {
       "name": "@superset-ui/legacy-preset-chart-nvd3",
       "version": "0.20.3",
@@ -52510,6 +52691,49 @@
         "react-dom": "^17.0.2"
       }
     },
+    "plugins/plugin-chart-point-cluster-map": {
+      "name": "@superset-ui/plugin-chart-point-cluster-map",
+      "version": "1.0.0",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@math.gl/web-mercator": "^4.1.0",
+        "mapbox-gl": "^3.0.0",
+        "maplibre-gl": "^5.0.0",
+        "react-map-gl": "^8.0.0",
+        "supercluster": "^8.0.1"
+      },
+      "peerDependencies": {
+        "@apache-superset/core": "*",
+        "@superset-ui/chart-controls": "*",
+        "@superset-ui/core": "*",
+        "react": "^17.0.2 || ^19.0.0",
+        "react-dom": "^17.0.2 || ^19.0.0"
+      }
+    },
+    "plugins/plugin-chart-point-cluster-map/node_modules/react-map-gl": {
+      "version": "8.1.0",
+      "resolved": "https://registry.npmjs.org/react-map-gl/-/react-map-gl-8.1.0.tgz",
+      "integrity": "sha512-vDx/QXR3Tb+8/ap/z6gdMjJQ8ZEyaZf6+uMSPz7jhWF5VZeIsKsGfPvwHVPPwGF43Ryn+YR4bd09uEFNR5OPdg==",
+      "license": "MIT",
+      "dependencies": {
+        "@vis.gl/react-mapbox": "8.1.0",
+        "@vis.gl/react-maplibre": "8.1.0"
+      },
+      "peerDependencies": {
+        "mapbox-gl": ">=1.13.0",
+        "maplibre-gl": ">=1.13.0",
+        "react": ">=16.3.0",
+        "react-dom": ">=16.3.0"
+      },
+      "peerDependenciesMeta": {
+        "mapbox-gl": {
+          "optional": true
+        },
+        "maplibre-gl": {
+          "optional": true
+        }
+      }
+    },
     "plugins/plugin-chart-table": {
       "name": "@superset-ui/plugin-chart-table",
       "version": "0.20.3",
@@ -52592,6 +52816,134 @@
         "d3-dispatch": "^1.0.3"
       }
     },
+    "plugins/preset-chart-deckgl": {
+      "name": "@superset-ui/preset-chart-deckgl",
+      "version": "1.0.0",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@deck.gl/aggregation-layers": "~9.2.11",
+        "@deck.gl/core": "~9.2.5",
+        "@deck.gl/extensions": "~9.2.9",
+        "@deck.gl/geo-layers": "~9.2.5",
+        "@deck.gl/layers": "~9.2.5",
+        "@deck.gl/mapbox": "~9.2.5",
+        "@deck.gl/mesh-layers": "~9.2.5",
+        "@luma.gl/constants": "~9.2.5",
+        "@luma.gl/core": "~9.2.5",
+        "@luma.gl/engine": "~9.2.6",
+        "@luma.gl/shadertools": "~9.2.6",
+        "@luma.gl/webgl": "~9.2.6",
+        "@mapbox/geojson-extent": "^1.0.1",
+        "@math.gl/web-mercator": "^4.1.0",
+        "@types/d3-array": "^3.2.2",
+        "@types/geojson": "^7946.0.16",
+        "bootstrap-slider": "^11.0.2",
+        "d3-array": "^3.2.4",
+        "d3-color": "^3.1.0",
+        "d3-scale": "^4.0.2",
+        "handlebars": "^4.7.9",
+        "lodash": "^4.18.1",
+        "maplibre-gl": "^5.0.0",
+        "mousetrap": "^1.6.5",
+        "ngeohash": "^0.6.3",
+        "prop-types": "^15.8.1",
+        "react-map-gl": "^8.0.0",
+        "underscore": "^1.13.7",
+        "urijs": "^1.19.11",
+        "xss": "^1.0.15"
+      },
+      "devDependencies": {
+        "@types/mapbox__geojson-extent": "^1.0.3",
+        "@types/ngeohash": "^0.6.8",
+        "@types/underscore": "^1.13.0",
+        "@types/urijs": "^1.19.26"
+      },
+      "peerDependencies": {
+        "@apache-superset/core": "*",
+        "@superset-ui/chart-controls": "*",
+        "@superset-ui/core": "*",
+        "dayjs": "^1.11.19",
+        "mapbox-gl": ">=1.0.0",
+        "react": "^17.0.2 || ^19.0.0",
+        "react-dom": "^17.0.2 || ^19.0.0"
+      },
+      "peerDependenciesMeta": {
+        "mapbox-gl": {
+          "optional": true
+        }
+      }
+    },
+    "plugins/preset-chart-deckgl/node_modules/@deck.gl/aggregation-layers": {
+      "version": "9.2.11",
+      "resolved": "https://registry.npmjs.org/@deck.gl/aggregation-layers/-/aggregation-layers-9.2.11.tgz",
+      "integrity": "sha512-MRFbBHtMcDkOthxXnMPm6nF08DjFDACaIQsJSyHkdWtLUTSLHsWnOTn/8QbB4ka86WyNyfJy3dibLu/m3ei2ow==",
+      "license": "MIT",
+      "dependencies": {
+        "@luma.gl/constants": "~9.2.6",
+        "@luma.gl/shadertools": "~9.2.6",
+        "@math.gl/core": "^4.1.0",
+        "@math.gl/web-mercator": "^4.1.0",
+        "d3-hexbin": "^0.2.1"
+      },
+      "peerDependencies": {
+        "@deck.gl/core": "~9.2.0",
+        "@deck.gl/layers": "~9.2.0",
+        "@luma.gl/core": "~9.2.6",
+        "@luma.gl/engine": "~9.2.6"
+      }
+    },
+    "plugins/preset-chart-deckgl/node_modules/@deck.gl/extensions": {
+      "version": "9.2.11",
+      "resolved": "https://registry.npmjs.org/@deck.gl/extensions/-/extensions-9.2.11.tgz",
+      "integrity": "sha512-zlpM4Bg1ifBziW1Juiii9NY5gyW2rEhyVTWnhagH/bpTCZ2E73OhnToYt1ouqmoxL6lMtIjhRXz6LPb7tJbHHQ==",
+      "license": "MIT",
+      "dependencies": {
+        "@luma.gl/constants": "~9.2.6",
+        "@luma.gl/shadertools": "~9.2.6",
+        "@math.gl/core": "^4.1.0"
+      },
+      "peerDependencies": {
+        "@deck.gl/core": "~9.2.0",
+        "@luma.gl/core": "~9.2.6",
+        "@luma.gl/engine": "~9.2.6"
+      }
+    },
+    "plugins/preset-chart-deckgl/node_modules/d3-array": {
+      "version": "3.2.4",
+      "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz",
+      "integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==",
+      "license": "ISC",
+      "dependencies": {
+        "internmap": "1 - 2"
+      },
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "plugins/preset-chart-deckgl/node_modules/react-map-gl": {
+      "version": "8.1.0",
+      "resolved": "https://registry.npmjs.org/react-map-gl/-/react-map-gl-8.1.0.tgz",
+      "integrity": "sha512-vDx/QXR3Tb+8/ap/z6gdMjJQ8ZEyaZf6+uMSPz7jhWF5VZeIsKsGfPvwHVPPwGF43Ryn+YR4bd09uEFNR5OPdg==",
+      "license": "MIT",
+      "dependencies": {
+        "@vis.gl/react-mapbox": "8.1.0",
+        "@vis.gl/react-maplibre": "8.1.0"
+      },
+      "peerDependencies": {
+        "mapbox-gl": ">=1.13.0",
+        "maplibre-gl": ">=1.13.0",
+        "react": ">=16.3.0",
+        "react-dom": ">=16.3.0"
+      },
+      "peerDependenciesMeta": {
+        "mapbox-gl": {
+          "optional": true
+        },
+        "maplibre-gl": {
+          "optional": true
+        }
+      }
+    },
     "tools/eslint-i18n-strings": {
       "version": "1.0.0",
       "extraneous": true,
diff --git a/superset-frontend/package.json b/superset-frontend/package.json
index 5ba1686e3ee..ea3f5310ec9 100644
--- a/superset-frontend/package.json
+++ b/superset-frontend/package.json
@@ -128,17 +128,17 @@
     "@superset-ui/legacy-plugin-chart-chord": "file:./plugins/legacy-plugin-chart-chord",
     "@superset-ui/legacy-plugin-chart-country-map": "file:./plugins/legacy-plugin-chart-country-map",
     "@superset-ui/legacy-plugin-chart-horizon": "file:./plugins/legacy-plugin-chart-horizon",
-    "@superset-ui/legacy-plugin-chart-map-box": "file:./plugins/legacy-plugin-chart-map-box",
     "@superset-ui/legacy-plugin-chart-paired-t-test": "file:./plugins/legacy-plugin-chart-paired-t-test",
     "@superset-ui/legacy-plugin-chart-parallel-coordinates": "file:./plugins/legacy-plugin-chart-parallel-coordinates",
     "@superset-ui/legacy-plugin-chart-partition": "file:./plugins/legacy-plugin-chart-partition",
     "@superset-ui/legacy-plugin-chart-rose": "file:./plugins/legacy-plugin-chart-rose",
     "@superset-ui/legacy-plugin-chart-world-map": "file:./plugins/legacy-plugin-chart-world-map",
-    "@superset-ui/legacy-preset-chart-deckgl": "file:./plugins/legacy-preset-chart-deckgl",
+    "@superset-ui/preset-chart-deckgl": "file:./plugins/preset-chart-deckgl",
     "@superset-ui/legacy-preset-chart-nvd3": "file:./plugins/legacy-preset-chart-nvd3",
     "@superset-ui/plugin-chart-ag-grid-table": "file:./plugins/plugin-chart-ag-grid-table",
     "@superset-ui/plugin-chart-cartodiagram": "file:./plugins/plugin-chart-cartodiagram",
     "@superset-ui/plugin-chart-echarts": "file:./plugins/plugin-chart-echarts",
+    "@superset-ui/plugin-chart-point-cluster-map": "file:./plugins/plugin-chart-point-cluster-map",
     "@superset-ui/plugin-chart-handlebars": "file:./plugins/plugin-chart-handlebars",
     "@superset-ui/plugin-chart-pivot-table": "file:./plugins/plugin-chart-pivot-table",
     "@superset-ui/plugin-chart-table": "file:./plugins/plugin-chart-table",
diff --git a/superset-frontend/packages/superset-ui-core/src/chart/types/VizType.ts b/superset-frontend/packages/superset-ui-core/src/chart/types/VizType.ts
index 0052f160927..cadb4c187ca 100644
--- a/superset-frontend/packages/superset-ui-core/src/chart/types/VizType.ts
+++ b/superset-frontend/packages/superset-ui-core/src/chart/types/VizType.ts
@@ -41,6 +41,7 @@ export enum VizType {
   LegacyBubble = 'bubble',
   Line = 'echarts_timeseries_line',
   MapBox = 'mapbox',
+  PointClusterMap = 'point_cluster_map',
   MixedTimeseries = 'mixed_timeseries',
   PairedTTest = 'paired_ttest',
   ParallelCoordinates = 'para',
diff --git a/superset-frontend/plugins/legacy-plugin-chart-map-box/CHANGELOG.md b/superset-frontend/plugins/legacy-plugin-chart-map-box/CHANGELOG.md
deleted file mode 100644
index a6f72afc0bf..00000000000
--- a/superset-frontend/plugins/legacy-plugin-chart-map-box/CHANGELOG.md
+++ /dev/null
@@ -1,55 +0,0 @@
-<!--
-  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.
--->
-
-# Change Log
-
-All notable changes to this project will be documented in this file.
-See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
-
-# [0.20.0](https://github.com/apache/superset/compare/v2021.41.0...v0.20.0) (2024-09-09)
-
-### Bug Fixes
-
-- **deckgl:** deckgl unable to load map ([#17851](https://github.com/apache/superset/issues/17851)) ([52f5dcb](https://github.com/apache/superset/commit/52f5dcb58eec7b188f4387b8781dcda4252a5680))
-- **explore:** Prevent shared controls from checking feature flags outside React render ([#21315](https://github.com/apache/superset/issues/21315)) ([2285ebe](https://github.com/apache/superset/commit/2285ebe72ec4edded6d195052740b7f9f13d1f1b))
-
-### Features
-
-- apply standardized form data to tier 2 charts ([#20530](https://github.com/apache/superset/issues/20530)) ([de524bc](https://github.com/apache/superset/commit/de524bc59f011fd361dcdb7d35c2cb51f7eba442))
-- **deckgl-map:** use an arbitraty Mabpox style URL ([#26027](https://github.com/apache/superset/issues/26027)) ([#26031](https://github.com/apache/superset/issues/26031)) ([af58784](https://github.com/apache/superset/commit/af587840403d83a7da7fb0f57bc10ad2335d4eeb))
-
-# [0.19.0](https://github.com/apache/superset/compare/v2021.41.0...v0.19.0) (2024-09-07)
-
-### Bug Fixes
-
-- **deckgl:** deckgl unable to load map ([#17851](https://github.com/apache/superset/issues/17851)) ([52f5dcb](https://github.com/apache/superset/commit/52f5dcb58eec7b188f4387b8781dcda4252a5680))
-- **explore:** Prevent shared controls from checking feature flags outside React render ([#21315](https://github.com/apache/superset/issues/21315)) ([2285ebe](https://github.com/apache/superset/commit/2285ebe72ec4edded6d195052740b7f9f13d1f1b))
-
-### Features
-
-- apply standardized form data to tier 2 charts ([#20530](https://github.com/apache/superset/issues/20530)) ([de524bc](https://github.com/apache/superset/commit/de524bc59f011fd361dcdb7d35c2cb51f7eba442))
-- **deckgl-map:** use an arbitraty Mabpox style URL ([#26027](https://github.com/apache/superset/issues/26027)) ([#26031](https://github.com/apache/superset/issues/26031)) ([af58784](https://github.com/apache/superset/commit/af587840403d83a7da7fb0f57bc10ad2335d4eeb))
-
-# [0.18.0](https://github.com/apache-superset/superset-ui/compare/v0.17.87...v0.18.0) (2021-08-30)
-
-**Note:** Version bump only for package @superset-ui/legacy-plugin-chart-map-box
-
-## [0.17.61](https://github.com/apache-superset/superset-ui/compare/v0.17.60...v0.17.61) (2021-07-02)
-
-**Note:** Version bump only for package @superset-ui/legacy-plugin-chart-map-box
diff --git a/superset-frontend/plugins/legacy-plugin-chart-map-box/README.md b/superset-frontend/plugins/legacy-plugin-chart-map-box/README.md
deleted file mode 100644
index d3ecc6bed40..00000000000
--- a/superset-frontend/plugins/legacy-plugin-chart-map-box/README.md
+++ /dev/null
@@ -1,52 +0,0 @@
-<!--
-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.
--->
-
-## @superset-ui/legacy-plugin-chart-map-box
-
-[![Version](https://img.shields.io/npm/v/@superset-ui/legacy-plugin-chart-map-box.svg?style=flat)](https://www.npmjs.com/package/@superset-ui/legacy-plugin-chart-map-box)
-[![Libraries.io](https://img.shields.io/librariesio/release/npm/%40superset-ui%2Flegacy-plugin-chart-map-box?style=flat)](https://libraries.io/npm/@superset-ui%2Flegacy-plugin-chart-map-box)
-
-This plugin provides MapBox for Superset.
-
-### Usage
-
-Configure `key`, which can be any `string`, and register the plugin. This `key` will be used to
-lookup this chart throughout the app.
-
-```js
-import MapBoxChartPlugin from '@superset-ui/legacy-plugin-chart-map-box';
-
-new MapBoxChartPlugin().configure({ key: 'map-box' }).register();
-```
-
-Then use it via `SuperChart`. See
-[storybook](https://apache-superset.github.io/superset-ui-plugins/?selectedKind=plugin-chart-map-box)
-for more details.
-
-```js
-<SuperChart
-  chartType="map-box"
-  width={600}
-  height={600}
-  formData={...}
-  queriesData={[{
-    data: {...},
-  }]}
-/>
-```
diff --git a/superset-frontend/plugins/legacy-plugin-chart-map-box/src/MapBox.tsx b/superset-frontend/plugins/legacy-plugin-chart-map-box/src/MapBox.tsx
deleted file mode 100644
index a69659de0b3..00000000000
--- a/superset-frontend/plugins/legacy-plugin-chart-map-box/src/MapBox.tsx
+++ /dev/null
@@ -1,243 +0,0 @@
-/**
- * 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.
- */
-/* eslint-disable react/jsx-sort-default-props, react/sort-prop-types */
-/* eslint-disable react/forbid-prop-types, react/require-default-props */
-import { Component } from 'react';
-import MapGL from 'react-map-gl';
-import { WebMercatorViewport } from '@math.gl/web-mercator';
-import ScatterPlotGlowOverlay from './ScatterPlotGlowOverlay';
-import './MapBox.css';
-
-const NOOP = () => {};
-export const DEFAULT_MAX_ZOOM = 16;
-export const DEFAULT_POINT_RADIUS = 60;
-
-interface Viewport {
-  longitude: number;
-  latitude: number;
-  zoom: number;
-  isDragging?: boolean;
-}
-
-interface Clusterer {
-  getClusters(bbox: number[], zoom: number): GeoJSONLocation[];
-}
-
-interface GeoJSONLocation {
-  geometry: {
-    coordinates: [number, number];
-  };
-  properties: Record<string, number | string | boolean | null | undefined>;
-}
-
-interface MapBoxProps {
-  width?: number;
-  height?: number;
-  aggregatorName?: string;
-  clusterer: Clusterer; // Required - used for getClusters()
-  globalOpacity?: number;
-  hasCustomMetric?: boolean;
-  mapStyle?: string;
-  mapboxApiKey: string;
-  onViewportChange?: (viewport: Viewport) => void;
-  pointRadius?: number;
-  pointRadiusUnit?: string;
-  renderWhileDragging?: boolean;
-  rgb?: (string | number)[];
-  bounds?: [[number, number], [number, number]]; // May be undefined for empty datasets
-  viewportLongitude?: number;
-  viewportLatitude?: number;
-  viewportZoom?: number;
-}
-
-interface MapBoxState {
-  viewport: Viewport;
-}
-
-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;
-
-  constructor(props: MapBoxProps) {
-    super(props);
-
-    const fitBounds = this.computeFitBoundsViewport();
-
-    this.state = {
-      viewport: this.mergeViewportWithProps(fitBounds),
-    };
-    this.handleViewportChange = this.handleViewportChange.bind(this);
-  }
-
-  handleViewportChange(viewport: Viewport) {
-    this.setState({ viewport });
-    const { onViewportChange } = this.props;
-    onViewportChange!(viewport);
-  }
-
-  mergeViewportWithProps(
-    fitBounds: Viewport,
-    viewport: Viewport = fitBounds,
-    props: MapBoxProps = this.props,
-    useFitBoundsForUnset = true,
-  ): Viewport {
-    const { viewportLongitude, viewportLatitude, viewportZoom } = props;
-
-    return {
-      ...viewport,
-      longitude:
-        viewportLongitude ??
-        (useFitBoundsForUnset ? fitBounds.longitude : viewport.longitude),
-      latitude:
-        viewportLatitude ??
-        (useFitBoundsForUnset ? fitBounds.latitude : viewport.latitude),
-      zoom:
-        viewportZoom ?? (useFitBoundsForUnset ? fitBounds.zoom : viewport.zoom),
-    };
-  }
-
-  computeFitBoundsViewport(): Viewport {
-    const { width = 400, height = 400, bounds } = this.props;
-    if (bounds && bounds[0] && bounds[1]) {
-      const mercator = new WebMercatorViewport({ width, height }).fitBounds(
-        bounds,
-      );
-      return {
-        latitude: mercator.latitude,
-        longitude: mercator.longitude,
-        zoom: mercator.zoom,
-      };
-    }
-    return { latitude: 0, longitude: 0, zoom: 1 };
-  }
-
-  componentDidUpdate(prevProps: MapBoxProps) {
-    const { viewport } = this.state;
-    const fitBoundsInputsChanged =
-      prevProps.width !== this.props.width ||
-      prevProps.height !== this.props.height ||
-      prevProps.bounds !== this.props.bounds;
-    const viewportPropsChanged =
-      prevProps.viewportLongitude !== this.props.viewportLongitude ||
-      prevProps.viewportLatitude !== this.props.viewportLatitude ||
-      prevProps.viewportZoom !== this.props.viewportZoom;
-
-    if (!fitBoundsInputsChanged && !viewportPropsChanged) {
-      return;
-    }
-
-    const fitBounds = this.computeFitBoundsViewport();
-    const nextViewport = this.mergeViewportWithProps(
-      fitBounds,
-      viewport,
-      this.props,
-      fitBoundsInputsChanged || viewportPropsChanged,
-    );
-
-    const viewportChanged =
-      nextViewport.longitude !== viewport.longitude ||
-      nextViewport.latitude !== viewport.latitude ||
-      nextViewport.zoom !== viewport.zoom;
-
-    if (viewportChanged) {
-      this.setState({ viewport: nextViewport });
-    }
-  }
-
-  render() {
-    const {
-      width,
-      height,
-      aggregatorName,
-      clusterer,
-      globalOpacity,
-      mapStyle,
-      mapboxApiKey,
-      pointRadius,
-      pointRadiusUnit,
-      renderWhileDragging,
-      rgb,
-      hasCustomMetric,
-      bounds,
-    } = this.props;
-    const { viewport } = this.state;
-    const isDragging =
-      viewport.isDragging === undefined ? false : viewport.isDragging;
-
-    // Compute the clusters based on the original bounds and current zoom level. Note when zoom/pan
-    // to an area outside of the original bounds, no additional queries are made to the backend to
-    // retrieve additional data.
-    // add this variable to widen the visible area
-    const offsetHorizontal = ((width ?? 400) * 0.5) / 100;
-    const offsetVertical = ((height ?? 400) * 0.5) / 100;
-
-    // Guard against empty datasets where bounds may be undefined
-    const bbox =
-      bounds && bounds[0] && bounds[1]
-        ? [
-            bounds[0][0] - offsetHorizontal,
-            bounds[0][1] - offsetVertical,
-            bounds[1][0] + offsetHorizontal,
-            bounds[1][1] + offsetVertical,
-          ]
-        : [-180, -90, 180, 90]; // Default to world bounds
-
-    const clusters = clusterer.getClusters(bbox, Math.round(viewport.zoom));
-
-    return (
-      <MapGL
-        {...viewport}
-        mapStyle={mapStyle}
-        width={width}
-        height={height}
-        mapboxApiAccessToken={mapboxApiKey}
-        onViewportChange={this.handleViewportChange}
-        preserveDrawingBuffer
-      >
-        <ScatterPlotGlowOverlay
-          {...viewport}
-          isDragging={isDragging}
-          locations={clusters}
-          dotRadius={pointRadius}
-          pointRadiusUnit={pointRadiusUnit}
-          rgb={rgb}
-          globalOpacity={globalOpacity}
-          compositeOperation="screen"
-          renderWhileDragging={renderWhileDragging}
-          aggregation={hasCustomMetric ? aggregatorName : undefined}
-          lngLatAccessor={(location: GeoJSONLocation) => {
-            const { coordinates } = location.geometry;
-
-            return [coordinates[0], coordinates[1]];
-          }}
-        />
-      </MapGL>
-    );
-  }
-}
-
-export default MapBox;
diff --git a/superset-frontend/plugins/legacy-plugin-chart-map-box/src/ScatterPlotGlowOverlay.tsx b/superset-frontend/plugins/legacy-plugin-chart-map-box/src/ScatterPlotGlowOverlay.tsx
deleted file mode 100644
index 2a5d87bb1c0..00000000000
--- a/superset-frontend/plugins/legacy-plugin-chart-map-box/src/ScatterPlotGlowOverlay.tsx
+++ /dev/null
@@ -1,425 +0,0 @@
-/**
- * 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.
- */
-/* eslint-disable react/require-default-props */
-import { PureComponent } from 'react';
-import { CanvasOverlay } from 'react-map-gl';
-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';
-
-// Shared radius bounds keep cluster and point sizing in sync.
-export const MIN_CLUSTER_RADIUS_RATIO = 1 / 6;
-export const MAX_POINT_RADIUS_RATIO = 1 / 3;
-
-interface GeoJSONLocation {
-  geometry: {
-    coordinates: [number, number];
-  };
-  properties: Record<string, number | string | boolean | null | undefined>;
-}
-
-interface RedrawParams {
-  width: number;
-  height: number;
-  ctx: CanvasRenderingContext2D;
-  isDragging: boolean;
-  project: (lngLat: [number, number]) => [number, number];
-}
-
-interface DrawTextOptions {
-  fontHeight?: number;
-  label?: string | number;
-  radius?: number;
-  rgb?: (string | number)[];
-  shadow?: boolean;
-}
-
-interface ScatterPlotGlowOverlayProps {
-  aggregation?: string;
-  compositeOperation?: string;
-  dotRadius?: number;
-  globalOpacity?: number;
-  lngLatAccessor?: (location: GeoJSONLocation) => [number, number];
-  locations: GeoJSONLocation[];
-  pointRadiusUnit?: string;
-  renderWhileDragging?: boolean;
-  rgb?: (string | number)[];
-  zoom?: number;
-  isDragging?: boolean;
-}
-
-const defaultProps: Partial<ScatterPlotGlowOverlayProps> = {
-  // Same as browser default.
-  compositeOperation: 'source-over',
-  dotRadius: 4,
-  lngLatAccessor: (location: GeoJSONLocation) => [
-    location.geometry.coordinates[0],
-    location.geometry.coordinates[1],
-  ],
-  renderWhileDragging: true,
-};
-
-const computeClusterLabel = (
-  properties: Record<string, number | string | boolean | null | undefined>,
-  aggregation: string | undefined,
-): number | string => {
-  const count = properties.point_count as number;
-  if (!aggregation) {
-    return count;
-  }
-  if (aggregation === 'sum' || aggregation === 'min' || aggregation === 'max') {
-    return properties[aggregation] as number;
-  }
-  const { sum } = properties as { sum: number };
-  const mean = sum / count;
-  if (aggregation === 'mean') {
-    return Math.round(100 * mean) / 100;
-  }
-  const { squaredSum } = properties as { squaredSum: number };
-  const variance = squaredSum / count - (sum / count) ** 2;
-  if (aggregation === 'var') {
-    return Math.round(100 * variance) / 100;
-  }
-  if (aggregation === 'stdev') {
-    return Math.round(100 * Math.sqrt(variance)) / 100;
-  }
-
-  // fallback to point_count, this really shouldn't happen
-  return count;
-};
-
-class ScatterPlotGlowOverlay extends PureComponent<ScatterPlotGlowOverlayProps> {
-  static defaultProps = defaultProps;
-
-  constructor(props: ScatterPlotGlowOverlayProps) {
-    super(props);
-    this.redraw = this.redraw.bind(this);
-  }
-
-  drawText(
-    ctx: CanvasRenderingContext2D,
-    pixel: [number, number],
-    options: DrawTextOptions = {},
-  ) {
-    const IS_DARK_THRESHOLD = 110;
-    const {
-      fontHeight = 0,
-      label = '',
-      radius = 0,
-      rgb = [0, 0, 0],
-      shadow = false,
-    } = options;
-    const maxWidth = radius * 1.8;
-    const luminance = luminanceFromRGB(
-      rgb[1] as number,
-      rgb[2] as number,
-      rgb[3] as number,
-    );
-
-    ctx.globalCompositeOperation = 'source-over';
-    ctx.fillStyle = luminance <= IS_DARK_THRESHOLD ? 'white' : 'black';
-    ctx.font = `${fontHeight}px sans-serif`;
-    ctx.textAlign = 'center';
-    ctx.textBaseline = 'middle';
-    if (shadow) {
-      ctx.shadowBlur = 15;
-      ctx.shadowColor = luminance <= IS_DARK_THRESHOLD ? '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') as GlobalCompositeOperation;
-    ctx.shadowBlur = 0;
-    ctx.shadowColor = '';
-  }
-
-  // Modified: https://github.com/uber/react-map-gl/blob/master/overlays/scatterplot.react.js
-  redraw({ width, height, ctx, isDragging, project }: RedrawParams) {
-    const {
-      aggregation,
-      compositeOperation,
-      dotRadius,
-      globalOpacity,
-      lngLatAccessor,
-      locations,
-      pointRadiusUnit,
-      renderWhileDragging,
-      rgb,
-      zoom,
-    } = this.props;
-
-    const radius = dotRadius ?? 4;
-    const clusterLabelMap: (number | string)[] = [];
-
-    locations.forEach((location, i) => {
-      if (location.properties.cluster) {
-        clusterLabelMap[i] = computeClusterLabel(
-          location.properties,
-          aggregation,
-        );
-      }
-    });
-
-    const finiteClusterLabels = clusterLabelMap
-      .map(value => Number(value))
-      .filter(value => Number.isFinite(value));
-    const safeMaxAbsLabel =
-      finiteClusterLabels.length > 0
-        ? Math.max(
-            Math.max(...finiteClusterLabels.map(value => Math.abs(value))),
-            1,
-          )
-        : 1;
-
-    // Calculate min/max radius values for Pixels mode scaling
-    let minRadiusValue = Infinity;
-    let maxRadiusValue = -Infinity;
-    if (pointRadiusUnit === 'Pixels') {
-      locations.forEach(location => {
-        // Accept both null and undefined as "no value" and coerce potential numeric strings
-        if (
-          !location.properties.cluster &&
-          location.properties.radius != null
-        ) {
-          const radiusValueRaw = location.properties.radius;
-          const radiusValue =
-            typeof radiusValueRaw === 'string' && radiusValueRaw.trim() === ''
-              ? null
-              : Number(radiusValueRaw);
-          if (radiusValue != null && Number.isFinite(radiusValue)) {
-            minRadiusValue = Math.min(minRadiusValue, radiusValue);
-            maxRadiusValue = Math.max(maxRadiusValue, radiusValue);
-          }
-        }
-      });
-    }
-
-    ctx.clearRect(0, 0, width, height);
-    ctx.globalCompositeOperation = (compositeOperation ??
-      'source-over') as GlobalCompositeOperation;
-
-    if ((renderWhileDragging || !isDragging) && locations) {
-      locations.forEach(function _forEach(
-        this: ScatterPlotGlowOverlay,
-        location: GeoJSONLocation,
-        i: number,
-      ) {
-        const pixel = project(lngLatAccessor!(location)) as [number, number];
-        const pixelRounded: [number, number] = [
-          roundDecimal(pixel[0], 1),
-          roundDecimal(pixel[1], 1),
-        ];
-
-        if (
-          pixelRounded[0] + radius >= 0 &&
-          pixelRounded[0] - radius < width &&
-          pixelRounded[1] + radius >= 0 &&
-          pixelRounded[1] - radius < height
-        ) {
-          ctx.beginPath();
-          if (location.properties.cluster) {
-            const clusterLabel = clusterLabelMap[i];
-            // Validate clusterLabel is a finite number before using it for radius calculation
-            const numericLabel = Number(clusterLabel);
-            const safeNumericLabel = Number.isFinite(numericLabel)
-              ? numericLabel
-              : 0;
-            const minClusterRadius =
-              pointRadiusUnit === 'Pixels'
-                ? radius * MAX_POINT_RADIUS_RATIO
-                : radius * MIN_CLUSTER_RADIUS_RATIO;
-            const ratio = Math.abs(safeNumericLabel) / safeMaxAbsLabel;
-            const scaledRadius = roundDecimal(
-              minClusterRadius + ratio ** 0.5 * (radius - minClusterRadius),
-              1,
-            );
-            const fontHeight = roundDecimal(scaledRadius * 0.5, 1);
-            const [x, y] = pixelRounded;
-            const gradient = ctx.createRadialGradient(
-              x,
-              y,
-              scaledRadius,
-              x,
-              y,
-              0,
-            );
-
-            gradient.addColorStop(
-              1,
-              `rgba(${rgb![1]}, ${rgb![2]}, ${rgb![3]}, ${0.8 * (globalOpacity ?? 1)})`,
-            );
-            gradient.addColorStop(
-              0,
-              `rgba(${rgb![1]}, ${rgb![2]}, ${rgb![3]}, 0)`,
-            );
-            ctx.arc(
-              pixelRounded[0],
-              pixelRounded[1],
-              scaledRadius,
-              0,
-              Math.PI * 2,
-            );
-            ctx.fillStyle = gradient;
-            ctx.fill();
-
-            if (Number.isFinite(safeNumericLabel)) {
-              let label: string | number = clusterLabel;
-              const absLabel = Math.abs(safeNumericLabel);
-              const sign = safeNumericLabel < 0 ? '-' : '';
-              if (absLabel >= 10000) {
-                label = `${sign}${Math.round(absLabel / 1000)}k`;
-              } else if (absLabel >= 1000) {
-                label = `${sign}${Math.round(absLabel / 100) / 10}k`;
-              }
-              this.drawText(ctx, pixelRounded, {
-                fontHeight,
-                label,
-                radius: scaledRadius,
-                rgb,
-                shadow: true,
-              });
-            }
-          } else {
-            const defaultRadius = radius * MIN_CLUSTER_RADIUS_RATIO;
-            const rawRadius = location.properties.radius;
-            const numericRadiusProperty =
-              rawRadius != null &&
-              !(typeof rawRadius === 'string' && rawRadius.trim() === '')
-                ? Number(rawRadius)
-                : null;
-            const radiusProperty =
-              numericRadiusProperty != null &&
-              Number.isFinite(numericRadiusProperty)
-                ? numericRadiusProperty
-                : null;
-            const pointMetric = location.properties.metric ?? null;
-            let pointRadius: number = radiusProperty ?? defaultRadius;
-            let pointLabel: string | number | undefined;
-
-            if (radiusProperty != null) {
-              const pointLatitude = lngLatAccessor!(location)[1];
-              if (pointRadiusUnit === 'Kilometers') {
-                pointLabel = `${roundDecimal(pointRadius, 2)}km`;
-                pointRadius = kmToPixels(pointRadius, pointLatitude, zoom ?? 0);
-              } else if (pointRadiusUnit === 'Miles') {
-                pointLabel = `${roundDecimal(pointRadius, 2)}mi`;
-                pointRadius = kmToPixels(
-                  pointRadius * MILES_PER_KM,
-                  pointLatitude,
-                  zoom ?? 0,
-                );
-              } else if (pointRadiusUnit === 'Pixels') {
-                // Scale pixel values to a reasonable range (radius/6 to radius/3)
-                // This ensures points are visible and proportional to their values
-                const MIN_POINT_RADIUS = radius * MIN_CLUSTER_RADIUS_RATIO;
-                const MAX_POINT_RADIUS = radius * MAX_POINT_RADIUS_RATIO;
-
-                if (
-                  Number.isFinite(minRadiusValue) &&
-                  Number.isFinite(maxRadiusValue) &&
-                  maxRadiusValue > minRadiusValue
-                ) {
-                  // Normalize the value to 0-1 range, then scale to pixel range
-                  const numericPointRadius = Number(pointRadius);
-                  if (!Number.isFinite(numericPointRadius)) {
-                    // fallback to minimum visible size when the value is not a finite number
-                    pointRadius = MIN_POINT_RADIUS;
-                  } else {
-                    const normalizedValueRaw =
-                      (numericPointRadius - minRadiusValue) /
-                      (maxRadiusValue - minRadiusValue);
-                    const normalizedValue = Math.max(
-                      0,
-                      Math.min(1, normalizedValueRaw),
-                    );
-                    pointRadius =
-                      MIN_POINT_RADIUS +
-                      normalizedValue * (MAX_POINT_RADIUS - MIN_POINT_RADIUS);
-                  }
-                  pointLabel = `${roundDecimal(radiusProperty, 2)}`;
-                } else if (
-                  Number.isFinite(minRadiusValue) &&
-                  minRadiusValue === maxRadiusValue
-                ) {
-                  // All values are the same, use a fixed medium size
-                  pointRadius = (MIN_POINT_RADIUS + MAX_POINT_RADIUS) / 2;
-                  pointLabel = `${roundDecimal(radiusProperty, 2)}`;
-                } else {
-                  // Use raw pixel values if they're already in a reasonable range
-                  pointRadius = Math.max(
-                    MIN_POINT_RADIUS,
-                    Math.min(pointRadius, MAX_POINT_RADIUS),
-                  );
-                  pointLabel = `${roundDecimal(radiusProperty, 2)}`;
-                }
-              }
-            }
-
-            if (pointMetric !== null) {
-              const numericMetric = parseFloat(String(pointMetric));
-              pointLabel = Number.isFinite(numericMetric)
-                ? roundDecimal(numericMetric, 2)
-                : String(pointMetric);
-            }
-
-            // Fall back to default points if pointRadius wasn't a numerical column
-            if (!pointRadius) {
-              pointRadius = defaultRadius;
-            }
-
-            ctx.arc(
-              pixelRounded[0],
-              pixelRounded[1],
-              roundDecimal(pointRadius, 1),
-              0,
-              Math.PI * 2,
-            );
-            ctx.fillStyle = `rgba(${rgb![1]}, ${rgb![2]}, ${rgb![3]}, ${globalOpacity})`;
-            ctx.fill();
-
-            if (pointLabel !== undefined) {
-              this.drawText(ctx, pixelRounded, {
-                fontHeight: roundDecimal(pointRadius, 1),
-                label: pointLabel,
-                radius: pointRadius,
-                rgb,
-                shadow: false,
-              });
-            }
-          }
-        }
-      }, this);
-    }
-  }
-
-  render() {
-    return <CanvasOverlay redraw={this.redraw} />;
-  }
-}
-
-export default ScatterPlotGlowOverlay;
diff --git a/superset-frontend/plugins/legacy-plugin-chart-map-box/src/stories/MapBox.stories.tsx b/superset-frontend/plugins/legacy-plugin-chart-map-box/src/stories/MapBox.stories.tsx
deleted file mode 100644
index 8f68419e769..00000000000
--- a/superset-frontend/plugins/legacy-plugin-chart-map-box/src/stories/MapBox.stories.tsx
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
- * 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.
- */
-
-/* eslint-disable no-magic-numbers */
-import { SuperChart } from '@superset-ui/core';
-import { useTheme } from '@apache-superset/core/theme';
-import MapBoxChartPlugin from '@superset-ui/legacy-plugin-chart-map-box';
-import { withResizableChartDemo } from '@storybook-shared';
-import { generateData } from './data';
-
-new MapBoxChartPlugin().configure({ key: 'map-box' }).register();
-
-export default {
-  title: 'Legacy Chart Plugins/legacy-plugin-chart-map-box',
-  decorators: [withResizableChartDemo],
-  args: {
-    clusteringRadius: 60,
-    globalOpacity: 1,
-    pointRadius: 'Auto',
-    renderWhileDragging: true,
-  },
-  argTypes: {
-    clusteringRadius: {
-      control: { type: 'range', min: 0, max: 200, step: 10 },
-      description: 'Radius in pixels for clustering points',
-    },
-    globalOpacity: {
-      control: { type: 'range', min: 0, max: 1, step: 0.1 },
-      description: 'Opacity of map markers',
-    },
-    pointRadius: {
-      control: 'select',
-      options: ['Auto', 1, 2, 5, 10, 20, 50],
-      description: 'Size of point markers',
-    },
-    renderWhileDragging: {
-      control: 'boolean',
-      description: 'Render markers while dragging the map',
-    },
-  },
-  parameters: {
-    docs: {
-      description: {
-        component:
-          'Note: This chart requires a Mapbox API key to display. Without a valid key, the map background will not render.',
-      },
-    },
-  },
-};
-
-export const MapBoxViz = ({
-  clusteringRadius,
-  globalOpacity,
-  pointRadius,
-  renderWhileDragging,
-  width,
-  height,
-}: {
-  clusteringRadius: number;
-  globalOpacity: number;
-  pointRadius: string | number;
-  renderWhileDragging: boolean;
-  width: number;
-  height: number;
-}) => {
-  const theme = useTheme();
-  return (
-    <SuperChart
-      chartType="map-box"
-      width={width}
-      height={height}
-      queriesData={[{ data: generateData(theme) }]}
-      formData={{
-        all_columns_x: 'LON',
-        all_columns_y: 'LAT',
-        clustering_radius: String(clusteringRadius),
-        global_opacity: globalOpacity,
-        mapbox_color: 'rgb(244, 176, 42)',
-        mapbox_label: [],
-        mapbox_style: 'mapbox://styles/mapbox/light-v9',
-        pandas_aggfunc: 'sum',
-        point_radius: pointRadius,
-        point_radius_unit: 'Pixels',
-        render_while_dragging: renderWhileDragging,
-        viewport_latitude: 37.78711146014447,
-        viewport_longitude: -122.37633433151713,
-        viewport_zoom: 10.026425338292782,
-      }}
-    />
-  );
-};
diff --git a/superset-frontend/plugins/legacy-plugin-chart-map-box/src/transformProps.ts b/superset-frontend/plugins/legacy-plugin-chart-map-box/src/transformProps.ts
deleted file mode 100644
index 45eca8d4448..00000000000
--- a/superset-frontend/plugins/legacy-plugin-chart-map-box/src/transformProps.ts
+++ /dev/null
@@ -1,162 +0,0 @@
-/**
- * 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 Supercluster, {
-  type Options as SuperclusterOptions,
-} from 'supercluster';
-import { ChartProps } from '@superset-ui/core';
-import { DEFAULT_POINT_RADIUS, DEFAULT_MAX_ZOOM } from './MapBox';
-
-const NOOP = () => {};
-const MIN_LONGITUDE = -180;
-const MAX_LONGITUDE = 180;
-const MIN_LATITUDE = -90;
-const MAX_LATITUDE = 90;
-const MIN_ZOOM = 0;
-
-function toFiniteNumber(
-  value: string | number | null | undefined,
-): number | undefined {
-  if (value === null || value === undefined) return undefined;
-  const normalizedValue = typeof value === 'string' ? value.trim() : value;
-  if (normalizedValue === '') return undefined;
-  const num = Number(normalizedValue);
-  return Number.isFinite(num) ? num : undefined;
-}
-
-function clampNumber(
-  value: number | undefined,
-  min: number,
-  max: number,
-): number | undefined {
-  if (value === undefined) return undefined;
-  return Math.min(max, Math.max(min, value));
-}
-
-interface ClusterProperties {
-  metric: number;
-  sum: number;
-  squaredSum: number;
-  min: number;
-  max: number;
-}
-
-export default function transformProps(chartProps: ChartProps) {
-  const { width, height, formData, hooks, queriesData } = chartProps;
-  const { onError = NOOP, setControlValue = NOOP } = hooks;
-  const { bounds, geoJSON, hasCustomMetric, mapboxApiKey } =
-    queriesData[0].data;
-  const {
-    clusteringRadius,
-    globalOpacity,
-    mapboxColor,
-    mapboxStyle,
-    pandasAggfunc,
-    pointRadiusUnit,
-    renderWhileDragging,
-    viewportLongitude,
-    viewportLatitude,
-    viewportZoom,
-  } = formData;
-
-  // Validate mapbox color
-  const rgb = /^rgb\((\d{1,3}),\s*(\d{1,3}),\s*(\d{1,3})\)$/.exec(mapboxColor);
-  if (rgb === null) {
-    onError("Color field must be of form 'rgb(%d, %d, %d)'");
-
-    return {};
-  }
-
-  const opts: SuperclusterOptions<ClusterProperties, ClusterProperties> = {
-    maxZoom: DEFAULT_MAX_ZOOM,
-    radius: clusteringRadius,
-  };
-  if (hasCustomMetric) {
-    opts.initial = () => ({
-      metric: 0,
-      sum: 0,
-      squaredSum: 0,
-      min: Infinity,
-      max: -Infinity,
-    });
-    opts.map = (prop: ClusterProperties) => ({
-      metric: prop.metric,
-      sum: prop.metric,
-      squaredSum: prop.metric ** 2,
-      min: prop.metric,
-      max: prop.metric,
-    });
-    opts.reduce = (accu: ClusterProperties, prop: ClusterProperties) => {
-      // Temporarily disable param-reassignment linting to work with supercluster's api
-      /* eslint-disable no-param-reassign */
-      accu.sum += prop.sum;
-      accu.squaredSum += prop.squaredSum;
-      accu.min = Math.min(accu.min, prop.min);
-      accu.max = Math.max(accu.max, prop.max);
-      /* eslint-enable no-param-reassign */
-    };
-  }
-  const clusterer = new Supercluster(opts);
-  clusterer.load(geoJSON.features);
-
-  return {
-    width,
-    height,
-    aggregatorName: pandasAggfunc,
-    bounds,
-    clusterer,
-    hasCustomMetric,
-    mapboxApiKey,
-    mapStyle: mapboxStyle,
-    onViewportChange({
-      latitude,
-      longitude,
-      zoom,
-    }: {
-      latitude: number;
-      longitude: number;
-      zoom: number;
-    }) {
-      setControlValue('viewport_longitude', longitude);
-      setControlValue('viewport_latitude', latitude);
-      setControlValue('viewport_zoom', zoom);
-    },
-    // Always use DEFAULT_POINT_RADIUS as the base radius for cluster sizing
-    // Individual point radii come from geoJSON properties.radius
-    pointRadius: DEFAULT_POINT_RADIUS,
-    pointRadiusUnit,
-    renderWhileDragging,
-    rgb,
-    viewportLongitude: clampNumber(
-      toFiniteNumber(viewportLongitude),
-      MIN_LONGITUDE,
-      MAX_LONGITUDE,
-    ),
-    viewportLatitude: clampNumber(
-      toFiniteNumber(viewportLatitude),
-      MIN_LATITUDE,
-      MAX_LATITUDE,
-    ),
-    viewportZoom: clampNumber(
-      toFiniteNumber(viewportZoom),
-      MIN_ZOOM,
-      DEFAULT_MAX_ZOOM,
-    ),
-    globalOpacity: Math.min(1, Math.max(0, toFiniteNumber(globalOpacity) ?? 1)),
-  };
-}
diff --git a/superset-frontend/plugins/legacy-plugin-chart-map-box/test/MapBox.test.tsx b/superset-frontend/plugins/legacy-plugin-chart-map-box/test/MapBox.test.tsx
deleted file mode 100644
index f0782f5fc14..00000000000
--- a/superset-frontend/plugins/legacy-plugin-chart-map-box/test/MapBox.test.tsx
+++ /dev/null
@@ -1,381 +0,0 @@
-/*
- * 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 { type ReactNode } from 'react';
-import { render } from '@testing-library/react';
-import MapBox from '../src/MapBox';
-
-// Capture the most recent viewport props passed to MapGL
-let lastMapGLProps: Record<string, unknown> = {};
-const mockFitBounds = jest.fn();
-
-jest.mock('react-map-gl', () => {
-  const MockMapGL = (props: Record<string, unknown>) => {
-    lastMapGLProps = props;
-    return <div data-test="map-gl">{props.children as ReactNode}</div>;
-  };
-  return { __esModule: true, default: MockMapGL };
-});
-
-jest.mock('@math.gl/web-mercator', () => ({
-  WebMercatorViewport: jest
-    .fn()
-    .mockImplementation(
-      ({ width, height }: { width: number; height: number }) => ({
-        fitBounds: (bounds: [[number, number], [number, number]]) =>
-          mockFitBounds(bounds, width, height),
-      }),
-    ),
-}));
-
-jest.mock('../src/ScatterPlotGlowOverlay', () => {
-  const MockOverlay = (props: Record<string, unknown>) => (
-    <div data-test="scatter-overlay" data-opacity={props.globalOpacity} />
-  );
-  return { __esModule: true, default: MockOverlay };
-});
-
-const defaultProps = {
-  width: 800,
-  height: 600,
-  clusterer: {
-    getClusters: jest.fn().mockReturnValue([]),
-  },
-  globalOpacity: 1,
-  mapboxApiKey: 'test-key',
-  mapStyle: 'mapbox://styles/mapbox/light-v9',
-  pointRadius: 60,
-  pointRadiusUnit: 'Pixels',
-  renderWhileDragging: true,
-  rgb: ['', 255, 0, 0] as (string | number)[],
-  hasCustomMetric: false,
-  bounds: [
-    [-74.0, 40.7],
-    [-73.9, 40.8],
-  ] as [[number, number], [number, number]],
-  onViewportChange: jest.fn(),
-};
-
-beforeEach(() => {
-  lastMapGLProps = {};
-  jest.clearAllMocks();
-  mockFitBounds.mockImplementation(
-    (
-      bounds: [[number, number], [number, number]],
-      width: number,
-      height: number,
-    ) => ({
-      latitude: Number(((bounds[0][1] + bounds[1][1]) / 2).toFixed(2)),
-      longitude: Number(((bounds[0][0] + bounds[1][0]) / 2).toFixed(2)),
-      zoom: Number((10 + width / 1000 + height / 10000).toFixed(2)),
-    }),
-  );
-});
-
-test('initializes viewport from bounds', () => {
-  render(<MapBox {...defaultProps} />);
-  expect(lastMapGLProps.latitude).toBe(40.75);
-  expect(lastMapGLProps.longitude).toBe(-73.95);
-  expect(lastMapGLProps.zoom).toBe(10.86);
-});
-
-test('updates viewport when viewport props change', () => {
-  const { rerender } = render(
-    <MapBox
-      {...defaultProps}
-      viewportLongitude={-73.95}
-      viewportLatitude={40.75}
-      viewportZoom={10}
-    />,
-  );
-
-  rerender(
-    <MapBox
-      {...defaultProps}
-      viewportLongitude={-122.4}
-      viewportLatitude={37.8}
-      viewportZoom={5}
-    />,
-  );
-
-  expect(lastMapGLProps.longitude).toBe(-122.4);
-  expect(lastMapGLProps.latitude).toBe(37.8);
-  expect(lastMapGLProps.zoom).toBe(5);
-});
-
-test('does not loop when viewport state matches new props', () => {
-  const { rerender } = render(
-    <MapBox
-      {...defaultProps}
-      viewportLongitude={-73.95}
-      viewportLatitude={40.75}
-      viewportZoom={10}
-    />,
-  );
-
-  // Re-render with same props that match the initial viewport state
-  rerender(
-    <MapBox
-      {...defaultProps}
-      viewportLongitude={-73.95}
-      viewportLatitude={40.75}
-      viewportZoom={10}
-    />,
-  );
-
-  // Viewport should still be the fitBounds-computed values since props didn't change
-  expect(lastMapGLProps.longitude).toBe(-73.95);
-  expect(lastMapGLProps.latitude).toBe(40.75);
-  expect(lastMapGLProps.zoom).toBe(10);
-});
-
-test('passes globalOpacity to ScatterPlotGlowOverlay', () => {
-  const { getByTestId } = render(
-    <MapBox {...defaultProps} globalOpacity={0.5} />,
-  );
-  const overlay = getByTestId('scatter-overlay');
-  expect(overlay.dataset.opacity).toBe('0.5');
-});
-
-test('initializes viewport from props when provided', () => {
-  render(
-    <MapBox
-      {...defaultProps}
-      viewportLongitude={-122.4}
-      viewportLatitude={37.8}
-      viewportZoom={5}
-    />,
-  );
-  expect(lastMapGLProps.longitude).toBe(-122.4);
-  expect(lastMapGLProps.latitude).toBe(37.8);
-  expect(lastMapGLProps.zoom).toBe(5);
-});
-
-test('handles undefined bounds gracefully', () => {
-  render(<MapBox {...defaultProps} bounds={undefined} />);
-  expect(lastMapGLProps.longitude).toBe(0);
-  expect(lastMapGLProps.latitude).toBe(0);
-  expect(lastMapGLProps.zoom).toBe(1);
-});
-
-test('applies partial viewport props on update', () => {
-  const { rerender } = render(<MapBox {...defaultProps} />);
-
-  rerender(<MapBox {...defaultProps} viewportLongitude={-122.4} />);
-
-  expect(lastMapGLProps.longitude).toBe(-122.4);
-  expect(lastMapGLProps.latitude).toBe(40.75);
-  expect(lastMapGLProps.zoom).toBe(10.86);
-});
-
-test('restores fitBounds when viewport props are cleared', () => {
-  const { rerender } = render(
-    <MapBox
-      {...defaultProps}
-      viewportLongitude={-122.4}
-      viewportLatitude={37.8}
-      viewportZoom={5}
-    />,
-  );
-
-  // Clear all viewport props (simulates user clearing the controls)
-  rerender(<MapBox {...defaultProps} />);
-
-  // Should revert to fitBounds values
-  expect(lastMapGLProps.longitude).toBe(-73.95);
-  expect(lastMapGLProps.latitude).toBe(40.75);
-  expect(lastMapGLProps.zoom).toBe(10.86);
-});
-
-test('restores only cleared viewport props, keeps the rest', () => {
-  const { rerender } = render(
-    <MapBox
-      {...defaultProps}
-      viewportLongitude={-122.4}
-      viewportLatitude={37.8}
-      viewportZoom={5}
-    />,
-  );
-
-  // Clear only longitude, keep lat/zoom
-  rerender(
-    <MapBox {...defaultProps} viewportLatitude={37.8} viewportZoom={5} />,
-  );
-
-  // Longitude reverts to fitBounds, lat/zoom stay
-  expect(lastMapGLProps.longitude).toBe(-73.95);
-  expect(lastMapGLProps.latitude).toBe(37.8);
-  expect(lastMapGLProps.zoom).toBe(5);
-});
-
-test('applies changed viewport props even when another is cleared simultaneously', () => {
-  const { rerender } = render(
-    <MapBox
-      {...defaultProps}
-      viewportLongitude={-122.4}
-      viewportLatitude={37.8}
-      viewportZoom={5}
-    />,
-  );
-
-  // Clear longitude, change latitude simultaneously
-  rerender(
-    <MapBox {...defaultProps} viewportLatitude={40.0} viewportZoom={5} />,
-  );
-
-  // Longitude reverts to fitBounds, latitude should be the NEW value
-  expect(lastMapGLProps.longitude).toBe(-73.95);
-  expect(lastMapGLProps.latitude).toBe(40.0);
-  expect(lastMapGLProps.zoom).toBe(5);
-});
-
-test('falls back to default viewport when cleared with undefined bounds', () => {
-  const { rerender } = render(
-    <MapBox
-      {...defaultProps}
-      bounds={undefined}
-      viewportLongitude={-122.4}
-      viewportLatitude={37.8}
-      viewportZoom={5}
-    />,
-  );
-
-  // Clear viewport props — no bounds to fitBounds to
-  rerender(<MapBox {...defaultProps} bounds={undefined} />);
-
-  // Should fall back to {0, 0, 1}
-  expect(lastMapGLProps.longitude).toBe(0);
-  expect(lastMapGLProps.latitude).toBe(0);
-  expect(lastMapGLProps.zoom).toBe(1);
-});
-
-test('recomputes fitBounds when bounds change and no explicit viewport is set', () => {
-  const { rerender } = render(<MapBox {...defaultProps} />);
-
-  rerender(
-    <MapBox
-      {...defaultProps}
-      bounds={[
-        [-123.2, 36.5],
-        [-121.8, 38.1],
-      ]}
-    />,
-  );
-
-  expect(lastMapGLProps.longitude).toBe(-122.5);
-  expect(lastMapGLProps.latitude).toBe(37.3);
-  expect(lastMapGLProps.zoom).toBe(10.86);
-});
-
-test('recomputes fitBounds when chart size changes and no explicit viewport is set', () => {
-  const { rerender } = render(<MapBox {...defaultProps} />);
-
-  rerender(<MapBox {...defaultProps} width={1200} height={900} />);
-
-  expect(lastMapGLProps.longitude).toBe(-73.95);
-  expect(lastMapGLProps.latitude).toBe(40.75);
-  expect(lastMapGLProps.zoom).toBe(11.29);
-});
-
-test('recomputes only implicit viewport fields when bounds change', () => {
-  const { rerender } = render(
-    <MapBox {...defaultProps} viewportLongitude={-122.4} />,
-  );
-
-  rerender(
-    <MapBox
-      {...defaultProps}
-      viewportLongitude={-122.4}
-      bounds={[
-        [-123.2, 36.5],
-        [-121.8, 38.1],
-      ]}
-    />,
-  );
-
-  expect(lastMapGLProps.longitude).toBe(-122.4);
-  expect(lastMapGLProps.latitude).toBe(37.3);
-  expect(lastMapGLProps.zoom).toBe(10.86);
-});
-
-test('recomputes only implicit viewport fields when chart size changes', () => {
-  const { rerender } = render(
-    <MapBox {...defaultProps} viewportLatitude={37.8} />,
-  );
-
-  rerender(
-    <MapBox
-      {...defaultProps}
-      viewportLatitude={37.8}
-      width={1200}
-      height={900}
-    />,
-  );
-
-  expect(lastMapGLProps.longitude).toBe(-73.95);
-  expect(lastMapGLProps.latitude).toBe(37.8);
-  expect(lastMapGLProps.zoom).toBe(11.29);
-});
-
-test('recomputes implicit position when zoom stays explicit across bounds changes', () => {
-  const { rerender } = render(<MapBox {...defaultProps} viewportZoom={5} />);
-
-  rerender(
-    <MapBox
-      {...defaultProps}
-      viewportZoom={5}
-      bounds={[
-        [-123.2, 36.5],
-        [-121.8, 38.1],
-      ]}
-    />,
-  );
-
-  expect(lastMapGLProps.longitude).toBe(-122.5);
-  expect(lastMapGLProps.latitude).toBe(37.3);
-  expect(lastMapGLProps.zoom).toBe(5);
-});
-
-test('does not recompute fitBounds on bounds change when an explicit viewport is set', () => {
-  const { rerender } = render(
-    <MapBox
-      {...defaultProps}
-      viewportLongitude={-122.4}
-      viewportLatitude={37.8}
-      viewportZoom={5}
-    />,
-  );
-
-  rerender(
-    <MapBox
-      {...defaultProps}
-      viewportLongitude={-122.4}
-      viewportLatitude={37.8}
-      viewportZoom={5}
-      bounds={[
-        [-123.2, 36.5],
-        [-121.8, 38.1],
-      ]}
-    />,
-  );
-
-  expect(lastMapGLProps.longitude).toBe(-122.4);
-  expect(lastMapGLProps.latitude).toBe(37.8);
-  expect(lastMapGLProps.zoom).toBe(5);
-});
diff --git a/superset-frontend/plugins/legacy-plugin-chart-map-box/test/tsconfig.json b/superset-frontend/plugins/legacy-plugin-chart-map-box/test/tsconfig.json
deleted file mode 100644
index 42fa49ba514..00000000000
--- a/superset-frontend/plugins/legacy-plugin-chart-map-box/test/tsconfig.json
+++ /dev/null
@@ -1,9 +0,0 @@
-{
-  "compilerOptions": {
-    "composite": false,
-    "emitDeclarationOnly": false,
-    "rootDir": "."
-  },
-  "extends": "../../../tsconfig.json",
-  "include": ["**/*", "../types/**/*", "../../../types/**/*"]
-}
diff --git a/superset-frontend/plugins/legacy-plugin-chart-map-box/test/utils/roundDecimal.test.ts b/superset-frontend/plugins/legacy-plugin-chart-map-box/test/utils/roundDecimal.test.ts
deleted file mode 100644
index a7fdda63a8a..00000000000
--- a/superset-frontend/plugins/legacy-plugin-chart-map-box/test/utils/roundDecimal.test.ts
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * 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 roundDecimal from '../../src/utils/roundDecimal';
-
-describe('roundDecimal', () => {
-  test('rounding method to limit the number of decimal digits', () => {
-    expect(roundDecimal(1.139, 2)).toBe(1.14);
-    expect(roundDecimal(1.13929, 3)).toBe(1.139);
-    expect(roundDecimal(1.13929)).toBe(1);
-  });
-});
diff --git a/superset-frontend/plugins/legacy-plugin-chart-map-box/tsconfig.json b/superset-frontend/plugins/legacy-plugin-chart-map-box/tsconfig.json
deleted file mode 100644
index 03190ec5bd4..00000000000
--- a/superset-frontend/plugins/legacy-plugin-chart-map-box/tsconfig.json
+++ /dev/null
@@ -1,25 +0,0 @@
-{
-  "extends": "../../tsconfig.json",
-  "compilerOptions": {
-    // Path Resolution: Override baseUrl to maintain correct path mappings from parent config
-    // (e.g., "@apache-superset/core" -> "./packages/superset-core/src")
-    "baseUrl": "../..",
-
-    // Directory Overrides: Parent config paths are relative to frontend root,
-    // but packages need paths relative to their own directory
-    "outDir": "lib",
-    "rootDir": "src",
-    "declarationDir": "lib"
-  },
-  "include": ["src/**/*.ts", "src/**/*.tsx", "types/**/*"],
-  "exclude": [
-    "src/**/*.js",
-    "src/**/*.jsx",
-    "src/**/*.test.*",
-    "src/**/*.stories.*"],
-  "references": [
-    { "path": "../../packages/superset-core" },
-    { "path": "../../packages/superset-ui-core" },
-    { "path": "../../packages/superset-ui-chart-controls" }
-  ]
-}
diff --git a/superset-frontend/plugins/legacy-plugin-chart-map-box/types/external.d.ts b/superset-frontend/plugins/legacy-plugin-chart-map-box/types/external.d.ts
deleted file mode 100644
index 89134501f25..00000000000
--- a/superset-frontend/plugins/legacy-plugin-chart-map-box/types/external.d.ts
+++ /dev/null
@@ -1,101 +0,0 @@
-/**
- * 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 'supercluster' {
-  interface Options<P = Record<string, unknown>, C = Record<string, unknown>> {
-    minZoom?: number;
-    maxZoom?: number;
-    minPoints?: number;
-    radius?: number;
-    extent?: number;
-    nodeSize?: number;
-    log?: boolean;
-    initial?: () => C;
-    map?: (props: P) => C;
-    reduce?: (accumulated: C, props: C) => void;
-  }
-
-  interface GeoJSONFeature {
-    type: string;
-    geometry: {
-      type: string;
-      coordinates: [number, number];
-    };
-    properties: Record<string, unknown>;
-  }
-
-  class Supercluster<P = Record<string, unknown>, C = Record<string, unknown>> {
-    constructor(options?: Options<P, C>);
-    load(points: GeoJSONFeature[]): Supercluster<P, C>;
-    getClusters(bbox: number[], zoom: number): GeoJSONFeature[];
-    getTile(z: number, x: number, y: number): GeoJSONFeature[] | null;
-    getChildren(clusterId: number): GeoJSONFeature[];
-    getLeaves(
-      clusterId: number,
-      limit?: number,
-      offset?: number,
-    ): GeoJSONFeature[];
-    getClusterExpansionZoom(clusterId: number): number;
-  }
-
-  export default Supercluster;
-  export { Options, GeoJSONFeature };
-}
-
-declare module 'react-map-gl' {
-  import { Component, ReactNode } from 'react';
-
-  interface MapGLProps {
-    width?: number;
-    height?: number;
-    latitude?: number;
-    longitude?: number;
-    zoom?: number;
-    mapStyle?: string;
-    mapboxApiAccessToken?: string;
-    onViewportChange?: Function;
-    preserveDrawingBuffer?: boolean;
-    children?: ReactNode;
-    [key: string]: unknown;
-  }
-
-  export default class MapGL extends Component<MapGLProps> {}
-
-  interface CanvasOverlayProps {
-    redraw: (params: {
-      width: number;
-      height: number;
-      ctx: CanvasRenderingContext2D;
-      isDragging: boolean;
-      project: (lngLat: [number, number]) => [number, number];
-    }) => void;
-  }
-
-  export class CanvasOverlay extends Component<CanvasOverlayProps> {}
-}
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/CHANGELOG.md b/superset-frontend/plugins/legacy-preset-chart-deckgl/CHANGELOG.md
deleted file mode 100644
index dbca781a9d7..00000000000
--- a/superset-frontend/plugins/legacy-preset-chart-deckgl/CHANGELOG.md
+++ /dev/null
@@ -1,89 +0,0 @@
-<!--
-  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.
--->
-
-# Change Log
-
-All notable changes to this project will be documented in this file.
-See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
-
-## [0.20.4](https://github.com/apache/superset/compare/v0.20.3...v0.20.4) (2024-12-10)
-
-**Note:** Version bump only for package @superset-ui/legacy-preset-chart-deckgl
-
-# [0.20.0](https://github.com/apache/superset/compare/v2021.41.0...v0.20.0) (2024-09-09)
-
-### Bug Fixes
-
-- **Dashboard:** Color inconsistency on refreshes and conflicts ([#27439](https://github.com/apache/superset/issues/27439)) ([313ee59](https://github.com/apache/superset/commit/313ee596f5435894f857d72be7269d5070c8c964))
-- deck.gl Geojson path not visible ([#24428](https://github.com/apache/superset/issues/24428)) ([6bb930e](https://github.com/apache/superset/commit/6bb930ef4ed26ea381e7f8e889851aa7867ba0eb))
-- deck.gl GeoJsonLayer Autozoom & fill/stroke options ([#19778](https://github.com/apache/superset/issues/19778)) ([d65b77e](https://github.com/apache/superset/commit/d65b77ec7dac4c2368fcaa1fe6e98db102966198))
-- **deck.gl Multiple Layer Chart:** Add Contour and Heatmap Layer as options ([#25923](https://github.com/apache/superset/issues/25923)) ([64ba579](https://github.com/apache/superset/commit/64ba5797df92d0f8067ccd2b30ba6ff58e0bd791))
-- deck.gl Scatterplot min/max radius ([#24363](https://github.com/apache/superset/issues/24363)) ([c728cdf](https://github.com/apache/superset/commit/c728cdf501ec292beb14a0982265052bf2274bec))
-- **deck.gl:** multiple layers map size is shrunk ([#18939](https://github.com/apache/superset/issues/18939)) ([2cb3635](https://github.com/apache/superset/commit/2cb3635256ee8e91f0bac2f3091684673c04ff2b))
-- **deck.gl:** update view state on property changes ([#17720](https://github.com/apache/superset/issues/17720)) ([#17826](https://github.com/apache/superset/issues/17826)) ([97d918b](https://github.com/apache/superset/commit/97d918b6927f572dca3b33c61b89c8b3ebdc4376))
-- DeckGL legend layout ([#30140](https://github.com/apache/superset/issues/30140)) ([af066a4](https://github.com/apache/superset/commit/af066a46306f2f476aa2944b14df3de1faf1e96d))
-- **deckgl:** deckgl unable to load map ([#17851](https://github.com/apache/superset/issues/17851)) ([52f5dcb](https://github.com/apache/superset/commit/52f5dcb58eec7b188f4387b8781dcda4252a5680))
-- **explore:** Fix chart standalone URL for report/thumbnail generation ([#20673](https://github.com/apache/superset/issues/20673)) ([84d4302](https://github.com/apache/superset/commit/84d4302628d18aa19c13cc5322e68abbc690ea4d))
-- **explore:** Prevent shared controls from checking feature flags outside React render ([#21315](https://github.com/apache/superset/issues/21315)) ([2285ebe](https://github.com/apache/superset/commit/2285ebe72ec4edded6d195052740b7f9f13d1f1b))
-- weight tooltip issue ([#19397](https://github.com/apache/superset/issues/19397)) ([f6d550b](https://github.com/apache/superset/commit/f6d550b7fc3643350483850064e65dbd3d026dc4))
-
-### Features
-
-- Add Deck.gl Contour Layer ([#24154](https://github.com/apache/superset/issues/24154)) ([512fb9a](https://github.com/apache/superset/commit/512fb9a0bdd428b94b0c121158b8b15b7631e0fb))
-- Add deck.gl Heatmap Visualization ([#23551](https://github.com/apache/superset/issues/23551)) ([fc8c537](https://github.com/apache/superset/commit/fc8c537118ce6c7b3a4624f88a31e2e7fb287327))
-- Add line width unit control in deckgl Polygon and Path ([#24755](https://github.com/apache/superset/issues/24755)) ([d26ea98](https://github.com/apache/superset/commit/d26ea980acc7d2a20757efc360d810afe83d5c65))
-- apply standardized form data to deckgl ([#20579](https://github.com/apache/superset/issues/20579)) ([290b89c](https://github.com/apache/superset/commit/290b89c7b4ae702c55f611bfac9cedb245ea8bd8))
-- **deck.gl:** add color range for deck.gl 3D ([#19520](https://github.com/apache/superset/issues/19520)) ([c0a00fd](https://github.com/apache/superset/commit/c0a00fd302ec66fbe0ca766cf73978c99ba00d82))
-- **deckgl-map:** use an arbitraty Mabpox style URL ([#26027](https://github.com/apache/superset/issues/26027)) ([#26031](https://github.com/apache/superset/issues/26031)) ([af58784](https://github.com/apache/superset/commit/af587840403d83a7da7fb0f57bc10ad2335d4eeb))
-- **explore:** Frontend implementation of dataset creation from infobox ([#19855](https://github.com/apache/superset/issues/19855)) ([ba0c37d](https://github.com/apache/superset/commit/ba0c37d3df85b1af39404af1d578daeb0ff2d278))
-- improve color consistency (save all labels) ([#19038](https://github.com/apache/superset/issues/19038)) ([dc57508](https://github.com/apache/superset/commit/dc575080d7e43d40b1734bb8f44fdc291cb95b11))
-- **legacy-preset-chart-deckgl:** Add ,.1f and ,.2f value formats to deckgl charts ([#18945](https://github.com/apache/superset/issues/18945)) ([c56dc8e](https://github.com/apache/superset/commit/c56dc8eace6a71b45240d1bb6768d75661052a2e))
-- make data tables support html ([#24368](https://github.com/apache/superset/issues/24368)) ([d2b0b8e](https://github.com/apache/superset/commit/d2b0b8eac52ad8b68639c6581a1ed174a593f564))
-- **viz picker:** Remove some tags, refactor Recommended section ([#27708](https://github.com/apache/superset/issues/27708)) ([c314999](https://github.com/apache/superset/commit/c3149994ac0d4392e0462421b62cd0c034142082))
-
-# [0.19.0](https://github.com/apache/superset/compare/v2021.41.0...v0.19.0) (2024-09-07)
-
-### Bug Fixes
-
-- **Dashboard:** Color inconsistency on refreshes and conflicts ([#27439](https://github.com/apache/superset/issues/27439)) ([313ee59](https://github.com/apache/superset/commit/313ee596f5435894f857d72be7269d5070c8c964))
-- deck.gl Geojson path not visible ([#24428](https://github.com/apache/superset/issues/24428)) ([6bb930e](https://github.com/apache/superset/commit/6bb930ef4ed26ea381e7f8e889851aa7867ba0eb))
-- deck.gl GeoJsonLayer Autozoom & fill/stroke options ([#19778](https://github.com/apache/superset/issues/19778)) ([d65b77e](https://github.com/apache/superset/commit/d65b77ec7dac4c2368fcaa1fe6e98db102966198))
-- **deck.gl Multiple Layer Chart:** Add Contour and Heatmap Layer as options ([#25923](https://github.com/apache/superset/issues/25923)) ([64ba579](https://github.com/apache/superset/commit/64ba5797df92d0f8067ccd2b30ba6ff58e0bd791))
-- deck.gl Scatterplot min/max radius ([#24363](https://github.com/apache/superset/issues/24363)) ([c728cdf](https://github.com/apache/superset/commit/c728cdf501ec292beb14a0982265052bf2274bec))
-- **deck.gl:** multiple layers map size is shrunk ([#18939](https://github.com/apache/superset/issues/18939)) ([2cb3635](https://github.com/apache/superset/commit/2cb3635256ee8e91f0bac2f3091684673c04ff2b))
-- **deck.gl:** update view state on property changes ([#17720](https://github.com/apache/superset/issues/17720)) ([#17826](https://github.com/apache/superset/issues/17826)) ([97d918b](https://github.com/apache/superset/commit/97d918b6927f572dca3b33c61b89c8b3ebdc4376))
-- DeckGL legend layout ([#30140](https://github.com/apache/superset/issues/30140)) ([af066a4](https://github.com/apache/superset/commit/af066a46306f2f476aa2944b14df3de1faf1e96d))
-- **deckgl:** deckgl unable to load map ([#17851](https://github.com/apache/superset/issues/17851)) ([52f5dcb](https://github.com/apache/superset/commit/52f5dcb58eec7b188f4387b8781dcda4252a5680))
-- **explore:** Fix chart standalone URL for report/thumbnail generation ([#20673](https://github.com/apache/superset/issues/20673)) ([84d4302](https://github.com/apache/superset/commit/84d4302628d18aa19c13cc5322e68abbc690ea4d))
-- **explore:** Prevent shared controls from checking feature flags outside React render ([#21315](https://github.com/apache/superset/issues/21315)) ([2285ebe](https://github.com/apache/superset/commit/2285ebe72ec4edded6d195052740b7f9f13d1f1b))
-- weight tooltip issue ([#19397](https://github.com/apache/superset/issues/19397)) ([f6d550b](https://github.com/apache/superset/commit/f6d550b7fc3643350483850064e65dbd3d026dc4))
-
-### Features
-
-- Add Deck.gl Contour Layer ([#24154](https://github.com/apache/superset/issues/24154)) ([512fb9a](https://github.com/apache/superset/commit/512fb9a0bdd428b94b0c121158b8b15b7631e0fb))
-- Add deck.gl Heatmap Visualization ([#23551](https://github.com/apache/superset/issues/23551)) ([fc8c537](https://github.com/apache/superset/commit/fc8c537118ce6c7b3a4624f88a31e2e7fb287327))
-- Add line width unit control in deckgl Polygon and Path ([#24755](https://github.com/apache/superset/issues/24755)) ([d26ea98](https://github.com/apache/superset/commit/d26ea980acc7d2a20757efc360d810afe83d5c65))
-- apply standardized form data to deckgl ([#20579](https://github.com/apache/superset/issues/20579)) ([290b89c](https://github.com/apache/superset/commit/290b89c7b4ae702c55f611bfac9cedb245ea8bd8))
-- **deck.gl:** add color range for deck.gl 3D ([#19520](https://github.com/apache/superset/issues/19520)) ([c0a00fd](https://github.com/apache/superset/commit/c0a00fd302ec66fbe0ca766cf73978c99ba00d82))
-- **deckgl-map:** use an arbitraty Mabpox style URL ([#26027](https://github.com/apache/superset/issues/26027)) ([#26031](https://github.com/apache/superset/issues/26031)) ([af58784](https://github.com/apache/superset/commit/af587840403d83a7da7fb0f57bc10ad2335d4eeb))
-- **explore:** Frontend implementation of dataset creation from infobox ([#19855](https://github.com/apache/superset/issues/19855)) ([ba0c37d](https://github.com/apache/superset/commit/ba0c37d3df85b1af39404af1d578daeb0ff2d278))
-- improve color consistency (save all labels) ([#19038](https://github.com/apache/superset/issues/19038)) ([dc57508](https://github.com/apache/superset/commit/dc575080d7e43d40b1734bb8f44fdc291cb95b11))
-- **legacy-preset-chart-deckgl:** Add ,.1f and ,.2f value formats to deckgl charts ([#18945](https://github.com/apache/superset/issues/18945)) ([c56dc8e](https://github.com/apache/superset/commit/c56dc8eace6a71b45240d1bb6768d75661052a2e))
-- make data tables support html ([#24368](https://github.com/apache/superset/issues/24368)) ([d2b0b8e](https://github.com/apache/superset/commit/d2b0b8eac52ad8b68639c6581a1ed174a593f564))
-- **viz picker:** Remove some tags, refactor Recommended section ([#27708](https://github.com/apache/superset/issues/27708)) ([c314999](https://github.com/apache/superset/commit/c3149994ac0d4392e0462421b62cd0c034142082))
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/README.md b/superset-frontend/plugins/legacy-preset-chart-deckgl/README.md
deleted file mode 100644
index f7455b355d3..00000000000
--- a/superset-frontend/plugins/legacy-preset-chart-deckgl/README.md
+++ /dev/null
@@ -1,57 +0,0 @@
-<!--
-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.
--->
-
-## @superset-ui/legacy-preset-chart-deckgl
-
-[![Version](https://img.shields.io/npm/v/@superset-ui/legacy-preset-chart-deckgl.svg?style=flat)](https://img.shields.io/npm/v/@superset-ui/legacy-preset-chart-deckgl.svg?style=flat-square)
-[![Libraries.io](https://img.shields.io/librariesio/release/npm/%40superset-ui%2Flegacy-preset-chart-deckgl?style=flat)](https://libraries.io/npm/@superset-ui%2Flegacy-preset-chart-deckgl)
-
-This plugin provides `deck.gl` for Superset.
-
-### Usage
-
-Import the preset and register. This will register all the chart plugins under `deck.gl`.
-
-```js
-import { DeckGLChartPreset } from '@superset-ui/legacy-preset-chart-deckgl';
-
-new DeckGLChartPreset().register();
-```
-
-or register charts one by one. Configure `key`, which can be any `string`, and register the plugin. This `key` will be used to lookup this chart throughout the app.
-
-```js
-import { ArcChartPlugin } from '@superset-ui/legacy-preset-chart-deckgl';
-
-new ArcChartPlugin().configure({ key: 'deck_arc' }).register();
-```
-
-Then use it via `SuperChart`. See [storybook](https://apache-superset.github.io/superset-ui-plugins-deckgl) for more details.
-
-```js
-<SuperChart
-  chartType="deck_arc"
-  width={600}
-  height={600}
-  formData={...}
-  queriesData={[{
-    data: {...},
-  }]}
-/>
-```
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/test/utils/colors.test.ts b/superset-frontend/plugins/legacy-preset-chart-deckgl/test/utils/colors.test.ts
deleted file mode 100644
index 4691ba489f2..00000000000
--- a/superset-frontend/plugins/legacy-preset-chart-deckgl/test/utils/colors.test.ts
+++ /dev/null
@@ -1,25 +0,0 @@
-/**
- * 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 { hexToRGB } from '../../src/utils/colors';
-
-describe('colors', () => {
-  test('hexToRGB()', () => {
-    expect(hexToRGB('#ffffff')).toEqual([255, 255, 255, 255]);
-  });
-});
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/test/utils/getPointsFromPolygon.test.ts b/superset-frontend/plugins/legacy-preset-chart-deckgl/test/utils/getPointsFromPolygon.test.ts
deleted file mode 100644
index f722db31fe4..00000000000
--- a/superset-frontend/plugins/legacy-preset-chart-deckgl/test/utils/getPointsFromPolygon.test.ts
+++ /dev/null
@@ -1,56 +0,0 @@
-/**
- * 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 getPointsFromPolygon from '../../src/utils/getPointsFromPolygon';
-
-describe('getPointsFromPolygon', () => {
-  test('handle original input', () => {
-    expect(
-      getPointsFromPolygon({
-        polygon: [
-          [1, 2],
-          [3, 4],
-        ],
-      }),
-    ).toEqual([
-      [1, 2],
-      [3, 4],
-    ]);
-  });
-  test('handle geojson features', () => {
-    expect(
-      getPointsFromPolygon({
-        polygon: {
-          type: 'Feature',
-          geometry: {
-            type: 'Polygon',
-            coordinates: [
-              [
-                [1, 2],
-                [3, 4],
-              ],
-            ],
-          },
-        },
-      }),
-    ).toEqual([
-      [1, 2],
-      [3, 4],
-    ]);
-  });
-});
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/types/external.d.ts b/superset-frontend/plugins/legacy-preset-chart-deckgl/types/external.d.ts
deleted file mode 100644
index 7093762a5c7..00000000000
--- a/superset-frontend/plugins/legacy-preset-chart-deckgl/types/external.d.ts
+++ /dev/null
@@ -1,71 +0,0 @@
-/**
- * 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 'deck.gl' {
-  import { Layer, LayerProps } from '@deck.gl/core';
-
-  interface HeatmapLayerProps<T extends object = any> extends LayerProps<T> {
-    id?: string;
-    data?: T[];
-    getPosition?: (d: T) => number[] | null | undefined;
-    getWeight?: (d: T) => number | null | undefined;
-    radiusPixels?: number;
-    colorRange?: number[][];
-    threshold?: number;
-    intensity?: number;
-    aggregation?: string;
-  }
-
-  interface ContourLayerProps<T extends object = any> extends LayerProps<T> {
-    id?: string;
-    data?: T[];
-    getPosition?: (d: T) => number[] | null | undefined;
-    getWeight?: (d: T) => number | null | undefined;
-    contours: {
-      color?: ColorType | undefined;
-      lowerThreshold?: any | undefined;
-      upperThreshold?: any | undefined;
-      strokeWidth?: any | undefined;
-      zIndex?: any | undefined;
-    };
-    cellSize: number;
-    colorRange?: number[][];
-    intensity?: number;
-    aggregation?: string;
-  }
-
-  export class HeatmapLayer<T extends object = any> extends Layer<
-    T,
-    HeatmapLayerProps<T>
-  > {
-    constructor(props: HeatmapLayerProps<T>);
-  }
-
-  export class ContourLayer<T extends object = any> extends Layer<
-    T,
-    ContourLayerProps<T>
-  > {
-    constructor(props: ContourLayerProps<T>);
-  }
-}
-
-declare module '*.png' {
-  const value: any;
-  export default value;
-}
diff --git a/superset-frontend/plugins/legacy-plugin-chart-map-box/package.json b/superset-frontend/plugins/plugin-chart-point-cluster-map/package.json
similarity index 65%
rename from superset-frontend/plugins/legacy-plugin-chart-map-box/package.json
rename to superset-frontend/plugins/plugin-chart-point-cluster-map/package.json
index 90b77fece55..3cfdcb0123e 100644
--- a/superset-frontend/plugins/legacy-plugin-chart-map-box/package.json
+++ b/superset-frontend/plugins/plugin-chart-point-cluster-map/package.json
@@ -1,7 +1,7 @@
 {
-  "name": "@superset-ui/legacy-plugin-chart-map-box",
-  "version": "0.20.3",
-  "description": "Superset Legacy Chart - MapBox",
+  "name": "@superset-ui/plugin-chart-point-cluster-map",
+  "version": "1.0.0",
+  "description": "Superset Chart Plugin - Point Cluster Map",
   "keywords": [
     "superset"
   ],
@@ -12,7 +12,7 @@
   "repository": {
     "type": "git",
     "url": "https://github.com/apache/superset.git",
-    "directory": "superset-frontend/plugins/legacy-plugin-chart-map-box"
+    "directory": "superset-frontend/plugins/plugin-chart-point-cluster-map"
   },
   "license": "Apache-2.0",
   "author": "Superset",
@@ -27,16 +27,17 @@
   ],
   "dependencies": {
     "@math.gl/web-mercator": "^4.1.0",
-    "prop-types": "^15.8.1",
-    "react-map-gl": "^6.1.19",
+    "mapbox-gl": "^3.0.0",
+    "maplibre-gl": "^5.0.0",
+    "react-map-gl": "^8.0.0",
     "supercluster": "^8.0.1"
   },
   "peerDependencies": {
+    "@apache-superset/core": "*",
     "@superset-ui/chart-controls": "*",
     "@superset-ui/core": "*",
-    "@apache-superset/core": "*",
-    "mapbox-gl": "*",
-    "react": "^17.0.2"
+    "react": "^17.0.2 || ^19.0.0",
+    "react-dom": "^17.0.2 || ^19.0.0"
   },
   "publishConfig": {
     "access": "public"
diff --git a/superset-frontend/plugins/legacy-plugin-chart-map-box/src/MapBox.css b/superset-frontend/plugins/plugin-chart-point-cluster-map/src/MapLibre.css
similarity index 96%
copy from superset-frontend/plugins/legacy-plugin-chart-map-box/src/MapBox.css
copy to superset-frontend/plugins/plugin-chart-point-cluster-map/src/MapLibre.css
index adade29f589..8d51367ab74 100644
--- a/superset-frontend/plugins/legacy-plugin-chart-map-box/src/MapBox.css
+++ b/superset-frontend/plugins/plugin-chart-point-cluster-map/src/MapLibre.css
@@ -16,6 +16,6 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-.mapbox .slice_container div {
+.maplibre .slice_container div {
   padding-top: 0px;
 }
diff --git a/superset-frontend/plugins/plugin-chart-point-cluster-map/src/MapLibre.tsx b/superset-frontend/plugins/plugin-chart-point-cluster-map/src/MapLibre.tsx
new file mode 100644
index 00000000000..c3c2b54db46
--- /dev/null
+++ b/superset-frontend/plugins/plugin-chart-point-cluster-map/src/MapLibre.tsx
@@ -0,0 +1,216 @@
+/**
+ * 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 { memo, useCallback, useEffect, useState } from 'react';
+import { Map as MapLibreMap } from 'react-map-gl/maplibre';
+import { Map as MapboxMap } from 'react-map-gl/mapbox';
+import { WebMercatorViewport } from '@math.gl/web-mercator';
+import { useTheme } from '@apache-superset/core/theme';
+import { t } from '@apache-superset/core/translation';
+import ScatterPlotOverlay from './components/ScatterPlotOverlay';
+import { getMapboxApiKey } from './utils/mapbox';
+import 'maplibre-gl/dist/maplibre-gl.css';
+import './MapLibre.css';
+
+const DEFAULT_MAP_STYLE = 'https://tiles.openfreemap.org/styles/liberty';
+
+export const DEFAULT_MAX_ZOOM = 16;
+export const DEFAULT_POINT_RADIUS = 60;
+
+interface Viewport {
+  longitude: number;
+  latitude: number;
+  zoom: number;
+}
+
+interface Clusterer {
+  getClusters(bbox: number[], zoom: number): GeoJSONLocation[];
+}
+
+interface GeoJSONLocation {
+  geometry: {
+    coordinates: [number, number];
+  };
+  properties: Record<string, number | string | boolean | null | undefined>;
+}
+
+interface MapLibreProps {
+  width?: number;
+  height?: number;
+  aggregatorName?: string;
+  clusterer: Clusterer; // Required - used for getClusters()
+  globalOpacity?: number;
+  hasCustomMetric?: boolean;
+  mapProvider?: string;
+  mapStyle?: string;
+  onViewportChange?: (viewport: Viewport) => void;
+  pointRadius?: number;
+  pointRadiusUnit?: string;
+  renderWhileDragging?: boolean;
+  rgb?: (string | number)[];
+  bounds?: [[number, number], [number, number]]; // May be undefined for empty datasets
+  viewportLongitude?: number;
+  viewportLatitude?: number;
+  viewportZoom?: number;
+}
+
+function MapLibre({
+  width = 400,
+  height = 400,
+  aggregatorName,
+  clusterer,
+  globalOpacity = 1,
+  hasCustomMetric,
+  mapProvider,
+  mapStyle,
+  onViewportChange,
+  pointRadius = DEFAULT_POINT_RADIUS,
+  pointRadiusUnit = 'Pixels',
+  renderWhileDragging = true,
+  rgb,
+  bounds,
+  viewportLongitude,
+  viewportLatitude,
+  viewportZoom,
+}: MapLibreProps) {
+  const computeFitBounds = useCallback((): Viewport => {
+    if (bounds && bounds[0] && bounds[1]) {
+      const mercator = new WebMercatorViewport({ width, height }).fitBounds(
+        bounds,
+      );
+      return {
+        latitude: mercator.latitude,
+        longitude: mercator.longitude,
+        zoom: mercator.zoom,
+      };
+    }
+    return { latitude: 0, longitude: 0, zoom: 1 };
+    // eslint-disable-next-line react-hooks/exhaustive-deps
+  }, []);
+
+  const mergeViewportWithProps = useCallback(
+    (fitBounds: Viewport, base: Viewport = fitBounds): Viewport => ({
+      ...base,
+      longitude: viewportLongitude ?? fitBounds.longitude,
+      latitude: viewportLatitude ?? fitBounds.latitude,
+      zoom: viewportZoom ?? fitBounds.zoom,
+    }),
+    [viewportLongitude, viewportLatitude, viewportZoom],
+  );
+
+  const [viewport, setViewport] = useState<Viewport>(() =>
+    mergeViewportWithProps(computeFitBounds()),
+  );
+
+  useEffect(() => {
+    const fitBounds = computeFitBounds();
+    const next = mergeViewportWithProps(fitBounds, viewport);
+    if (
+      next.longitude !== viewport.longitude ||
+      next.latitude !== viewport.latitude ||
+      next.zoom !== viewport.zoom
+    ) {
+      setViewport(next);
+    }
+    // Only re-run when the viewport-override props change
+    // eslint-disable-next-line react-hooks/exhaustive-deps
+  }, [viewportLongitude, viewportLatitude, viewportZoom]);
+
+  const handleMove = useCallback(
+    (evt: {
+      viewState: { longitude: number; latitude: number; zoom: number };
+    }) => {
+      const { longitude, latitude, zoom } = evt.viewState;
+      const newViewport = { longitude, latitude, zoom };
+      setViewport(newViewport);
+      onViewportChange?.(newViewport);
+    },
+    [onViewportChange],
+  );
+
+  // add this variable to widen the visible area
+  const offsetHorizontal = (width * 0.5) / 100;
+  const offsetVertical = (height * 0.5) / 100;
+
+  const bbox =
+    bounds && bounds[0] && bounds[1]
+      ? [
+          bounds[0][0] - offsetHorizontal,
+          bounds[0][1] - offsetVertical,
+          bounds[1][0] + offsetHorizontal,
+          bounds[1][1] + offsetVertical,
+        ]
+      : [-180, -90, 180, 90];
+
+  const clusters = clusterer.getClusters(bbox, Math.round(viewport.zoom));
+
+  const theme = useTheme();
+  const resolvedMapStyle = mapStyle || DEFAULT_MAP_STYLE;
+  const mapboxApiKey = mapProvider === 'mapbox' ? getMapboxApiKey() : '';
+
+  if (mapProvider === 'mapbox' && !mapboxApiKey) {
+    return (
+      <div
+        style={{
+          width,
+          height,
+          display: 'flex',
+          alignItems: 'center',
+          justifyContent: 'center',
+          padding: 16,
+          textAlign: 'center',
+          color: theme.colorTextSecondary,
+        }}
+      >
+        {t('Mapbox requires a MAPBOX_API_KEY to be configured on the server.')}
+      </div>
+    );
+  }
+
+  const MapComponent = mapProvider === 'mapbox' ? MapboxMap : MapLibreMap;
+  const mapboxProps =
+    mapProvider === 'mapbox' ? { mapboxAccessToken: mapboxApiKey } : {};
+
+  return (
+    <MapComponent
+      {...viewport}
+      {...mapboxProps}
+      style={{ width, height }}
+      mapStyle={resolvedMapStyle}
+      onMove={handleMove}
+    >
+      <ScatterPlotOverlay
+        locations={clusters}
+        dotRadius={pointRadius}
+        pointRadiusUnit={pointRadiusUnit}
+        rgb={rgb}
+        globalOpacity={globalOpacity}
+        compositeOperation="screen"
+        renderWhileDragging={renderWhileDragging}
+        aggregation={hasCustomMetric ? aggregatorName : undefined}
+        zoom={viewport.zoom}
+        lngLatAccessor={(location: GeoJSONLocation) => {
+          const { coordinates } = location.geometry;
+          return [coordinates[0], coordinates[1]];
+        }}
+      />
+    </MapComponent>
+  );
+}
+
+export default memo(MapLibre);
diff --git a/superset-frontend/plugins/plugin-chart-point-cluster-map/src/buildQuery.ts b/superset-frontend/plugins/plugin-chart-point-cluster-map/src/buildQuery.ts
new file mode 100644
index 00000000000..6bc9a72cc92
--- /dev/null
+++ b/superset-frontend/plugins/plugin-chart-point-cluster-map/src/buildQuery.ts
@@ -0,0 +1,90 @@
+/**
+ * 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 {
+  buildQueryContext,
+  ensureIsArray,
+  QueryFormColumn,
+  QueryObject,
+  QueryObjectFilterClause,
+  SqlaFormData,
+} from '@superset-ui/core';
+
+export interface MapLibreFormData extends SqlaFormData {
+  all_columns_x?: string;
+  all_columns_y?: string;
+  map_label?: string[];
+  point_radius?: string;
+  clustering_radius?: string;
+  pandas_aggfunc?: string;
+  global_opacity?: number;
+  maplibre_style?: string;
+  mapbox_style?: string;
+  map_color?: string;
+  render_while_dragging?: boolean;
+  point_radius_unit?: string;
+}
+
+export default function buildQuery(formData: MapLibreFormData) {
+  const { all_columns_x, all_columns_y, map_label, point_radius } = formData;
+
+  if (!all_columns_x || !all_columns_y) {
+    throw new Error('Longitude and latitude columns are required');
+  }
+
+  return buildQueryContext(formData, (baseQueryObject: QueryObject) => {
+    const columns: QueryFormColumn[] = [
+      ...ensureIsArray(baseQueryObject.columns || []),
+      all_columns_x,
+      all_columns_y,
+    ];
+
+    // Add label column if specified and not 'count'
+    const hasCustomMetric =
+      map_label && map_label.length > 0 && map_label[0] !== 'count';
+    if (hasCustomMetric) {
+      columns.push(map_label[0]);
+    }
+
+    // Add point radius column if not "Auto"
+    if (point_radius && point_radius !== 'Auto') {
+      columns.push(point_radius);
+    }
+
+    // Add null filters for lon/lat
+    const filters: QueryObjectFilterClause[] = ensureIsArray(
+      baseQueryObject.filters || [],
+    );
+    filters.push(
+      { col: all_columns_x, op: 'IS NOT NULL' },
+      { col: all_columns_y, op: 'IS NOT NULL' },
+    );
+
+    // Deduplicate columns
+    const uniqueColumns = [...new Set(columns)];
+
+    return [
+      {
+        ...baseQueryObject,
+        columns: uniqueColumns,
+        filters,
+        is_timeseries: false,
+      },
+    ];
+  });
+}
diff --git a/superset-frontend/plugins/plugin-chart-point-cluster-map/src/components/CanvasOverlay.tsx b/superset-frontend/plugins/plugin-chart-point-cluster-map/src/components/CanvasOverlay.tsx
new file mode 100644
index 00000000000..91b26922cf6
--- /dev/null
+++ b/superset-frontend/plugins/plugin-chart-point-cluster-map/src/components/CanvasOverlay.tsx
@@ -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 { useCallback, useEffect, useRef } from 'react';
+import { useMap as useMapLibre } from 'react-map-gl/maplibre';
+import { useMap as useMapbox } from 'react-map-gl/mapbox';
+
+export interface RedrawParams {
+  width: number;
+  height: number;
+  ctx: CanvasRenderingContext2D;
+  isDragging: boolean;
+  project: (lngLat: [number, number]) => [number, number];
+}
+
+interface CanvasOverlayProps {
+  redraw: (params: RedrawParams) => void;
+}
+
+export default function CanvasOverlay({ redraw }: CanvasOverlayProps) {
+  const mapLibreContext = useMapLibre();
+  const mapboxContext = useMapbox();
+  const mapRef = (mapLibreContext.current ?? mapboxContext.current) as any;
+  const canvasRef = useRef<HTMLCanvasElement>(null);
+  const isDraggingRef = useRef(false);
+
+  const project = useCallback(
+    (lngLat: [number, number]): [number, number] => {
+      if (!mapRef) return [0, 0];
+      const map = mapRef.getMap();
+      const point = map.project(lngLat);
+      return [point.x, point.y];
+    },
+    [mapRef],
+  );
+
+  const performRedraw = useCallback(() => {
+    const canvas = canvasRef.current;
+    const map = mapRef?.getMap();
+    if (!canvas || !map) return;
+
+    const container = map.getContainer();
+    const dpr = window.devicePixelRatio || 1;
+    const width = container.clientWidth;
+    const height = container.clientHeight;
+
+    canvas.width = width * dpr;
+    canvas.height = height * dpr;
+    canvas.style.width = `${width}px`;
+    canvas.style.height = `${height}px`;
+
+    const ctx = canvas.getContext('2d');
+    if (!ctx) return;
+
+    ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
+
+    redraw({
+      width,
+      height,
+      ctx,
+      isDragging: isDraggingRef.current,
+      project,
+    });
+  }, [mapRef, redraw, project]);
+
+  useEffect(() => {
+    const map = mapRef?.getMap();
+    if (!map) return undefined;
+
+    const onMove = () => performRedraw();
+    const onDragStart = () => {
+      isDraggingRef.current = true;
+    };
+    const onDragEnd = () => {
+      isDraggingRef.current = false;
+      performRedraw();
+    };
+    const onResize = () => performRedraw();
+
+    map.on('move', onMove);
+    map.on('dragstart', onDragStart);
+    map.on('dragend', onDragEnd);
+    map.on('resize', onResize);
+
+    performRedraw();
+
+    return () => {
+      map.off('move', onMove);
+      map.off('dragstart', onDragStart);
+      map.off('dragend', onDragEnd);
+      map.off('resize', onResize);
+    };
+  }, [mapRef, performRedraw]);
+
+  return (
+    <canvas
+      ref={canvasRef}
+      style={{
+        position: 'absolute',
+        top: 0,
+        left: 0,
+        pointerEvents: 'none',
+      }}
+    />
+  );
+}
diff --git a/superset-frontend/plugins/plugin-chart-point-cluster-map/src/components/ScatterPlotOverlay.tsx b/superset-frontend/plugins/plugin-chart-point-cluster-map/src/components/ScatterPlotOverlay.tsx
new file mode 100644
index 00000000000..5c94a69b514
--- /dev/null
+++ b/superset-frontend/plugins/plugin-chart-point-cluster-map/src/components/ScatterPlotOverlay.tsx
@@ -0,0 +1,400 @@
+/**
+ * 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 { memo, useCallback } from 'react';
+import CanvasOverlay, { type RedrawParams } from './CanvasOverlay';
+import { kmToPixels, MILES_PER_KM } from '../utils/geo';
+import roundDecimal from '../utils/roundDecimal';
+import luminanceFromRGB from '../utils/luminanceFromRGB';
+
+// Shared radius bounds keep cluster and point sizing in sync.
+export const MIN_CLUSTER_RADIUS_RATIO = 1 / 6;
+export const MAX_POINT_RADIUS_RATIO = 1 / 3;
+
+interface GeoJSONLocation {
+  geometry: {
+    coordinates: [number, number];
+  };
+  properties: Record<string, number | string | boolean | null | undefined>;
+}
+
+interface DrawTextOptions {
+  fontHeight?: number;
+  label?: string | number;
+  radius?: number;
+  rgb?: (string | number)[];
+  shadow?: boolean;
+}
+
+interface ScatterPlotOverlayProps {
+  aggregation?: string;
+  compositeOperation?: string;
+  dotRadius?: number;
+  globalOpacity?: number;
+  lngLatAccessor?: (location: GeoJSONLocation) => [number, number];
+  locations: GeoJSONLocation[];
+  pointRadiusUnit?: string;
+  renderWhileDragging?: boolean;
+  rgb?: (string | number)[];
+  zoom?: number;
+}
+
+const IS_DARK_THRESHOLD = 110;
+
+const defaultLngLatAccessor = (location: GeoJSONLocation): [number, number] => [
+  location.geometry.coordinates[0],
+  location.geometry.coordinates[1],
+];
+
+const computeClusterLabel = (
+  properties: Record<string, number | string | boolean | null | undefined>,
+  aggregation: string | undefined,
+): number | string => {
+  const count = properties.point_count as number;
+  if (!aggregation) {
+    return count;
+  }
+  if (aggregation === 'sum' || aggregation === 'min' || aggregation === 'max') {
+    return properties[aggregation] as number;
+  }
+  const { sum } = properties as { sum: number };
+  const mean = sum / count;
+  if (aggregation === 'mean') {
+    return Math.round(100 * mean) / 100;
+  }
+  const { squaredSum } = properties as { squaredSum: number };
+  const variance = squaredSum / count - (sum / count) ** 2;
+  if (aggregation === 'var') {
+    return Math.round(100 * variance) / 100;
+  }
+  if (aggregation === 'std' || aggregation === 'stdev') {
+    return Math.round(100 * Math.sqrt(variance)) / 100;
+  }
+
+  // fallback to point_count
+  return count;
+};
+
+function drawText(
+  ctx: CanvasRenderingContext2D,
+  pixel: [number, number],
+  compositeOperation: string,
+  options: DrawTextOptions = {},
+) {
+  const {
+    fontHeight = 0,
+    label = '',
+    radius = 0,
+    rgb = [0, 0, 0],
+    shadow = false,
+  } = options;
+  const maxWidth = radius * 1.8;
+  const luminance = luminanceFromRGB(
+    rgb[1] as number,
+    rgb[2] as number,
+    rgb[3] as number,
+  );
+
+  ctx.globalCompositeOperation = 'source-over';
+  ctx.fillStyle = luminance <= IS_DARK_THRESHOLD ? 'white' : 'black';
+  ctx.font = `${fontHeight}px sans-serif`;
+  ctx.textAlign = 'center';
+  ctx.textBaseline = 'middle';
+  if (shadow) {
+    ctx.shadowBlur = 15;
+    ctx.shadowColor = luminance <= IS_DARK_THRESHOLD ? 'black' : '';
+  }
+
+  const textWidth = ctx.measureText(String(label)).width;
+  if (textWidth > maxWidth) {
+    const scale = fontHeight / textWidth;
+    ctx.font = `${scale * maxWidth}px sans-serif`;
+  }
+
+  ctx.fillText(String(label), pixel[0], pixel[1]);
+  ctx.globalCompositeOperation = compositeOperation as GlobalCompositeOperation;
+  ctx.shadowBlur = 0;
+  ctx.shadowColor = '';
+}
+
+function ScatterPlotOverlay({
+  aggregation,
+  compositeOperation = 'source-over',
+  dotRadius = 4,
+  globalOpacity = 1,
+  lngLatAccessor = defaultLngLatAccessor,
+  locations,
+  pointRadiusUnit,
+  renderWhileDragging = true,
+  rgb,
+  zoom,
+}: ScatterPlotOverlayProps) {
+  const redraw = useCallback(
+    ({ width, height, ctx, isDragging, project }: RedrawParams) => {
+      const radius = dotRadius;
+      const clusterLabelMap: (number | string)[] = [];
+
+      locations.forEach((location, i) => {
+        if (location.properties.cluster) {
+          clusterLabelMap[i] = computeClusterLabel(
+            location.properties,
+            aggregation,
+          );
+        }
+      });
+
+      const finiteClusterLabels = clusterLabelMap
+        .map(value => Number(value))
+        .filter(value => Number.isFinite(value));
+      const safeMaxAbsLabel =
+        finiteClusterLabels.length > 0
+          ? Math.max(
+              Math.max(...finiteClusterLabels.map(value => Math.abs(value))),
+              1,
+            )
+          : 1;
+
+      // Calculate min/max radius values for Pixels mode scaling
+      let minRadiusValue = Infinity;
+      let maxRadiusValue = -Infinity;
+      if (pointRadiusUnit === 'Pixels') {
+        locations.forEach(location => {
+          if (
+            !location.properties.cluster &&
+            location.properties.radius != null
+          ) {
+            const radiusValueRaw = location.properties.radius;
+            const radiusValue =
+              typeof radiusValueRaw === 'string' && radiusValueRaw.trim() === ''
+                ? null
+                : Number(radiusValueRaw);
+            if (radiusValue != null && Number.isFinite(radiusValue)) {
+              minRadiusValue = Math.min(minRadiusValue, radiusValue);
+              maxRadiusValue = Math.max(maxRadiusValue, radiusValue);
+            }
+          }
+        });
+      }
+
+      ctx.clearRect(0, 0, width, height);
+      ctx.globalCompositeOperation =
+        compositeOperation as GlobalCompositeOperation;
+
+      if ((renderWhileDragging || !isDragging) && locations) {
+        locations.forEach((location: GeoJSONLocation, i: number) => {
+          const pixel = project(lngLatAccessor(location));
+          const pixelRounded: [number, number] = [
+            roundDecimal(pixel[0], 1),
+            roundDecimal(pixel[1], 1),
+          ];
+
+          if (
+            pixelRounded[0] + radius >= 0 &&
+            pixelRounded[0] - radius < width &&
+            pixelRounded[1] + radius >= 0 &&
+            pixelRounded[1] - radius < height
+          ) {
+            ctx.beginPath();
+            if (location.properties.cluster) {
+              const clusterLabel = clusterLabelMap[i];
+              const numericLabel = Number(clusterLabel);
+              const safeNumericLabel = Number.isFinite(numericLabel)
+                ? numericLabel
+                : 0;
+              const minClusterRadius =
+                pointRadiusUnit === 'Pixels'
+                  ? radius * MAX_POINT_RADIUS_RATIO
+                  : radius * MIN_CLUSTER_RADIUS_RATIO;
+              const ratio = Math.abs(safeNumericLabel) / safeMaxAbsLabel;
+              const scaledRadius = roundDecimal(
+                minClusterRadius + ratio ** 0.5 * (radius - minClusterRadius),
+                1,
+              );
+              const fontHeight = roundDecimal(scaledRadius * 0.5, 1);
+              const [x, y] = pixelRounded;
+              const gradient = ctx.createRadialGradient(
+                x,
+                y,
+                scaledRadius,
+                x,
+                y,
+                0,
+              );
+
+              gradient.addColorStop(
+                1,
+                `rgba(${rgb![1]}, ${rgb![2]}, ${rgb![3]}, ${0.8 * globalOpacity})`,
+              );
+              gradient.addColorStop(
+                0,
+                `rgba(${rgb![1]}, ${rgb![2]}, ${rgb![3]}, 0)`,
+              );
+              ctx.arc(
+                pixelRounded[0],
+                pixelRounded[1],
+                scaledRadius,
+                0,
+                Math.PI * 2,
+              );
+              ctx.fillStyle = gradient;
+              ctx.fill();
+
+              if (Number.isFinite(safeNumericLabel)) {
+                let label: string | number = clusterLabel;
+                const absLabel = Math.abs(safeNumericLabel);
+                const sign = safeNumericLabel < 0 ? '-' : '';
+                if (absLabel >= 10000) {
+                  label = `${sign}${Math.round(absLabel / 1000)}k`;
+                } else if (absLabel >= 1000) {
+                  label = `${sign}${Math.round(absLabel / 100) / 10}k`;
+                }
+                drawText(ctx, pixelRounded, compositeOperation, {
+                  fontHeight,
+                  label,
+                  radius: scaledRadius,
+                  rgb,
+                  shadow: true,
+                });
+              }
+            } else {
+              const defaultRadius = radius * MIN_CLUSTER_RADIUS_RATIO;
+              const rawRadius = location.properties.radius;
+              const numericRadiusProperty =
+                rawRadius != null &&
+                !(typeof rawRadius === 'string' && rawRadius.trim() === '')
+                  ? Number(rawRadius)
+                  : null;
+              const radiusProperty =
+                numericRadiusProperty != null &&
+                Number.isFinite(numericRadiusProperty)
+                  ? numericRadiusProperty
+                  : null;
+              const pointMetric = location.properties.metric ?? null;
+              let pointRadius: number = radiusProperty ?? defaultRadius;
+              let pointLabel: string | number | undefined;
+
+              if (radiusProperty != null) {
+                const pointLatitude = lngLatAccessor(location)[1];
+                if (pointRadiusUnit === 'Kilometers') {
+                  pointLabel = `${roundDecimal(pointRadius, 2)}km`;
+                  pointRadius = kmToPixels(
+                    pointRadius,
+                    pointLatitude,
+                    zoom ?? 0,
+                  );
+                } else if (pointRadiusUnit === 'Miles') {
+                  pointLabel = `${roundDecimal(pointRadius, 2)}mi`;
+                  pointRadius = kmToPixels(
+                    pointRadius * MILES_PER_KM,
+                    pointLatitude,
+                    zoom ?? 0,
+                  );
+                } else if (pointRadiusUnit === 'Pixels') {
+                  const MIN_POINT_RADIUS = radius * MIN_CLUSTER_RADIUS_RATIO;
+                  const MAX_POINT_RADIUS = radius * MAX_POINT_RADIUS_RATIO;
+
+                  if (
+                    Number.isFinite(minRadiusValue) &&
+                    Number.isFinite(maxRadiusValue) &&
+                    maxRadiusValue > minRadiusValue
+                  ) {
+                    const numericPointRadius = Number(pointRadius);
+                    if (!Number.isFinite(numericPointRadius)) {
+                      pointRadius = MIN_POINT_RADIUS;
+                    } else {
+                      const normalizedValueRaw =
+                        (numericPointRadius - minRadiusValue) /
+                        (maxRadiusValue - minRadiusValue);
+                      const normalizedValue = Math.max(
+                        0,
+                        Math.min(1, normalizedValueRaw),
+                      );
+                      pointRadius =
+                        MIN_POINT_RADIUS +
+                        normalizedValue * (MAX_POINT_RADIUS - MIN_POINT_RADIUS);
+                    }
+                    pointLabel = `${roundDecimal(radiusProperty, 2)}`;
+                  } else if (
+                    Number.isFinite(minRadiusValue) &&
+                    minRadiusValue === maxRadiusValue
+                  ) {
+                    pointRadius = (MIN_POINT_RADIUS + MAX_POINT_RADIUS) / 2;
+                    pointLabel = `${roundDecimal(radiusProperty, 2)}`;
+                  } else {
+                    pointRadius = Math.max(
+                      MIN_POINT_RADIUS,
+                      Math.min(pointRadius, MAX_POINT_RADIUS),
+                    );
+                    pointLabel = `${roundDecimal(radiusProperty, 2)}`;
+                  }
+                }
+              }
+
+              if (pointMetric !== null) {
+                const numericMetric = parseFloat(String(pointMetric));
+                pointLabel = Number.isFinite(numericMetric)
+                  ? roundDecimal(numericMetric, 2)
+                  : String(pointMetric);
+              }
+
+              if (!pointRadius) {
+                pointRadius = defaultRadius;
+              }
+
+              ctx.arc(
+                pixelRounded[0],
+                pixelRounded[1],
+                roundDecimal(pointRadius, 1),
+                0,
+                Math.PI * 2,
+              );
+              ctx.fillStyle = `rgba(${rgb![1]}, ${rgb![2]}, ${rgb![3]}, ${globalOpacity})`;
+              ctx.fill();
+
+              if (pointLabel !== undefined) {
+                drawText(ctx, pixelRounded, compositeOperation, {
+                  fontHeight: roundDecimal(pointRadius, 1),
+                  label: pointLabel,
+                  radius: pointRadius,
+                  rgb,
+                  shadow: false,
+                });
+              }
+            }
+          }
+        });
+      }
+    },
+    [
+      aggregation,
+      compositeOperation,
+      dotRadius,
+      globalOpacity,
+      lngLatAccessor,
+      locations,
+      pointRadiusUnit,
+      renderWhileDragging,
+      rgb,
+      zoom,
+    ],
+  );
+
+  return <CanvasOverlay redraw={redraw} />;
+}
+
+export default memo(ScatterPlotOverlay);
diff --git a/superset-frontend/plugins/legacy-plugin-chart-map-box/src/controlPanel.ts b/superset-frontend/plugins/plugin-chart-point-cluster-map/src/controlPanel.ts
similarity index 73%
rename from superset-frontend/plugins/legacy-plugin-chart-map-box/src/controlPanel.ts
rename to superset-frontend/plugins/plugin-chart-point-cluster-map/src/controlPanel.ts
index 8b1c846e048..294e4f12087 100644
--- a/superset-frontend/plugins/legacy-plugin-chart-map-box/src/controlPanel.ts
+++ b/superset-frontend/plugins/plugin-chart-point-cluster-map/src/controlPanel.ts
@@ -17,7 +17,6 @@
  * under the License.
  */
 import { t } from '@apache-superset/core/translation';
-import { validateMapboxStylesUrl } from '@superset-ui/core';
 import {
   columnChoices,
   ControlPanelConfig,
@@ -29,12 +28,12 @@ import {
 const columnsConfig = sharedControls.entity;
 
 const colorChoices = [
-  ['rgb(0, 139, 139)', t('Dark Cyan')],
-  ['rgb(128, 0, 128)', t('Purple')],
-  ['rgb(255, 215, 0)', t('Gold')],
-  ['rgb(69, 69, 69)', t('Dim Gray')],
-  ['rgb(220, 20, 60)', t('Crimson')],
-  ['rgb(34, 139, 34)', t('Forest Green')],
+  ['#008b8b', t('Dark Cyan')],
+  ['#800080', t('Purple')],
+  ['#ffd700', t('Gold')],
+  ['#454545', t('Dim Gray')],
+  ['#dc143c', t('Crimson')],
+  ['#228b22', t('Forest Green')],
 ];
 
 const config: ControlPanelConfig = {
@@ -110,7 +109,7 @@ const config: ControlPanelConfig = {
                   'Either a numerical column or `Auto`, which scales the point based ' +
                   'on the largest cluster',
               ),
-              mapStateToProps: state => {
+              mapStateToProps: (state: any) => {
                 const datasourceChoices = columnChoices(state.datasource);
                 const choices: [string, string][] = [['Auto', t('Auto')]];
                 return {
@@ -145,7 +144,7 @@ const config: ControlPanelConfig = {
       controlSetRows: [
         [
           {
-            name: 'mapbox_label',
+            name: 'map_label',
             config: {
               type: 'SelectControl',
               multi: true,
@@ -157,7 +156,7 @@ const config: ControlPanelConfig = {
                   'Non-numerical columns will be used to label points. ' +
                   'Leave empty to get a count of points in each cluster.',
               ),
-              mapStateToProps: state => ({
+              mapStateToProps: (state: any) => ({
                 choices: columnChoices(state.datasource),
               }),
             },
@@ -189,21 +188,66 @@ const config: ControlPanelConfig = {
       ],
     },
     {
-      label: t('Visual Tweaks'),
+      label: t('Map'),
+      tabOverride: 'customize',
+      expanded: true,
       controlSetRows: [
         [
           {
-            name: 'render_while_dragging',
+            name: 'map_renderer',
             config: {
-              type: 'CheckboxControl',
-              label: t('Live render'),
-              default: true,
+              type: 'SelectControl',
+              label: t('Map Renderer'),
+              clearable: false,
+              renderTrigger: true,
+              choices: [
+                ['maplibre', t('MapLibre (open-source)')],
+                ['mapbox', t('Mapbox (API key required)')],
+              ],
+              default: 'maplibre',
               description: t(
-                'Points and clusters will update as the viewport is being changed',
+                'MapLibre is open-source and requires no API key. Mapbox requires MAPBOX_API_KEY to be configured on the server.',
               ),
             },
           },
         ],
+        [
+          {
+            name: 'maplibre_style',
+            config: {
+              type: 'SelectControl',
+              label: t('Map Style'),
+              clearable: false,
+              renderTrigger: true,
+              freeForm: true,
+              choices: [
+                [
+                  'https://tiles.openfreemap.org/styles/liberty',
+                  t('Liberty (OpenFreeMap)'),
+                ],
+                [
+                  'https://basemaps.cartocdn.com/gl/positron-gl-style/style.json',
+                  t('Light (Carto)'),
+                ],
+                [
+                  'https://basemaps.cartocdn.com/gl/dark-matter-gl-style/style.json',
+                  t('Dark (Carto)'),
+                ],
+                [
+                  'https://basemaps.cartocdn.com/gl/voyager-gl-style/style.json',
+                  t('Streets (Carto)'),
+                ],
+              ],
+              default: 'https://tiles.openfreemap.org/styles/liberty',
+              description: t(
+                'Base layer map style. See MapLibre documentation: %s',
+                'https://maplibre.org/maplibre-style-spec/',
+              ),
+              visibility: ({ controls }: any) =>
+                controls?.map_renderer?.value !== 'mapbox',
+            },
+          },
+        ],
         [
           {
             name: 'mapbox_style',
@@ -213,22 +257,42 @@ const config: ControlPanelConfig = {
               clearable: false,
               renderTrigger: true,
               freeForm: true,
-              validators: [validateMapboxStylesUrl],
               choices: [
-                ['mapbox://styles/mapbox/streets-v9', t('Streets')],
-                ['mapbox://styles/mapbox/dark-v9', t('Dark')],
-                ['mapbox://styles/mapbox/light-v9', t('Light')],
+                ['mapbox://styles/mapbox/streets-v12', t('Streets')],
+                ['mapbox://styles/mapbox/outdoors-v12', t('Outdoors')],
+                ['mapbox://styles/mapbox/light-v11', t('Light')],
+                ['mapbox://styles/mapbox/dark-v11', t('Dark')],
+                ['mapbox://styles/mapbox/satellite-v9', t('Satellite')],
                 [
-                  'mapbox://styles/mapbox/satellite-streets-v9',
+                  'mapbox://styles/mapbox/satellite-streets-v12',
                   t('Satellite Streets'),
                 ],
-                ['mapbox://styles/mapbox/satellite-v9', t('Satellite')],
-                ['mapbox://styles/mapbox/outdoors-v9', t('Outdoors')],
               ],
-              default: 'mapbox://styles/mapbox/light-v9',
+              default: 'mapbox://styles/mapbox/light-v11',
               description: t(
-                'Base layer map style. See Mapbox documentation: %s',
-                'https://docs.mapbox.com/help/glossary/style-url/',
+                'Base layer map style. Accepts a Mapbox style URL (mapbox://styles/...).',
+              ),
+              visibility: ({ controls }: any) =>
+                controls?.map_renderer?.value === 'mapbox',
+            },
+          },
+        ],
+      ],
+    },
+    {
+      label: t('Visual Tweaks'),
+      tabOverride: 'customize',
+      controlSetRows: [
+        [
+          {
+            name: 'render_while_dragging',
+            config: {
+              type: 'CheckboxControl',
+              label: t('Live render'),
+              renderTrigger: true,
+              default: true,
+              description: t(
+                'Points and clusters will update as the viewport is being changed',
               ),
             },
           },
@@ -239,9 +303,9 @@ const config: ControlPanelConfig = {
             config: {
               type: 'TextControl',
               label: t('Opacity'),
+              renderTrigger: true,
               default: 1,
               isFloat: true,
-              renderTrigger: true,
               description: t(
                 'Opacity of all clusters, points, and labels. Between 0 and 1.',
               ),
@@ -250,10 +314,11 @@ const config: ControlPanelConfig = {
         ],
         [
           {
-            name: 'mapbox_color',
+            name: 'map_color',
             config: {
               type: 'SelectControl',
               freeForm: true,
+              renderTrigger: true,
               label: t('RGB Color'),
               default: colorChoices[0][0],
               choices: colorChoices,
@@ -278,7 +343,6 @@ const config: ControlPanelConfig = {
               isFloat: true,
               description: t('Longitude of default viewport'),
               places: 8,
-              // Viewport longitude changes shouldn't prompt user to re-run query
               dontRefreshOnChange: true,
             },
           },
@@ -292,7 +356,6 @@ const config: ControlPanelConfig = {
               isFloat: true,
               description: t('Latitude of default viewport'),
               places: 8,
-              // Viewport latitude changes shouldn't prompt user to re-run query
               dontRefreshOnChange: true,
             },
           },
@@ -308,7 +371,6 @@ const config: ControlPanelConfig = {
               default: '',
               description: t('Zoom level of the map'),
               places: 8,
-              // Viewport zoom shouldn't prompt user to re-run query
               dontRefreshOnChange: true,
             },
           },
@@ -325,7 +387,7 @@ const config: ControlPanelConfig = {
       ),
     },
   },
-  formDataOverrides: formData => ({
+  formDataOverrides: (formData: any) => ({
     ...formData,
     groupby: getStandardizedControls().popAllColumns(),
   }),
diff --git a/superset-frontend/plugins/legacy-plugin-chart-map-box/src/images/MapBox-dark.jpg b/superset-frontend/plugins/plugin-chart-point-cluster-map/src/images/MapBox-dark.jpg
similarity index 100%
rename from superset-frontend/plugins/legacy-plugin-chart-map-box/src/images/MapBox-dark.jpg
rename to superset-frontend/plugins/plugin-chart-point-cluster-map/src/images/MapBox-dark.jpg
diff --git a/superset-frontend/plugins/legacy-plugin-chart-map-box/src/images/MapBox.jpg b/superset-frontend/plugins/plugin-chart-point-cluster-map/src/images/MapBox.jpg
similarity index 100%
rename from superset-frontend/plugins/legacy-plugin-chart-map-box/src/images/MapBox.jpg
rename to superset-frontend/plugins/plugin-chart-point-cluster-map/src/images/MapBox.jpg
diff --git a/superset-frontend/plugins/legacy-plugin-chart-map-box/src/images/MapBox2-dark.jpg b/superset-frontend/plugins/plugin-chart-point-cluster-map/src/images/MapBox2-dark.jpg
similarity index 100%
rename from superset-frontend/plugins/legacy-plugin-chart-map-box/src/images/MapBox2-dark.jpg
rename to superset-frontend/plugins/plugin-chart-point-cluster-map/src/images/MapBox2-dark.jpg
diff --git a/superset-frontend/plugins/legacy-plugin-chart-map-box/src/images/MapBox2.jpg b/superset-frontend/plugins/plugin-chart-point-cluster-map/src/images/MapBox2.jpg
similarity index 100%
rename from superset-frontend/plugins/legacy-plugin-chart-map-box/src/images/MapBox2.jpg
rename to superset-frontend/plugins/plugin-chart-point-cluster-map/src/images/MapBox2.jpg
diff --git a/superset-frontend/plugins/legacy-plugin-chart-map-box/src/images/thumbnail-dark.png b/superset-frontend/plugins/plugin-chart-point-cluster-map/src/images/thumbnail-dark.png
similarity index 100%
rename from superset-frontend/plugins/legacy-plugin-chart-map-box/src/images/thumbnail-dark.png
rename to superset-frontend/plugins/plugin-chart-point-cluster-map/src/images/thumbnail-dark.png
diff --git a/superset-frontend/plugins/legacy-plugin-chart-map-box/src/images/thumbnail.png b/superset-frontend/plugins/plugin-chart-point-cluster-map/src/images/thumbnail.png
similarity index 100%
rename from superset-frontend/plugins/legacy-plugin-chart-map-box/src/images/thumbnail.png
rename to superset-frontend/plugins/plugin-chart-point-cluster-map/src/images/thumbnail.png
diff --git a/superset-frontend/plugins/legacy-plugin-chart-map-box/src/images/thumbnailLarge.png b/superset-frontend/plugins/plugin-chart-point-cluster-map/src/images/thumbnailLarge.png
similarity index 100%
rename from superset-frontend/plugins/legacy-plugin-chart-map-box/src/images/thumbnailLarge.png
rename to superset-frontend/plugins/plugin-chart-point-cluster-map/src/images/thumbnailLarge.png
diff --git a/superset-frontend/plugins/legacy-plugin-chart-map-box/src/index.ts b/superset-frontend/plugins/plugin-chart-point-cluster-map/src/index.ts
similarity index 88%
rename from superset-frontend/plugins/legacy-plugin-chart-map-box/src/index.ts
rename to superset-frontend/plugins/plugin-chart-point-cluster-map/src/index.ts
index 666e8ea2303..1f95c36a98d 100644
--- a/superset-frontend/plugins/legacy-plugin-chart-map-box/src/index.ts
+++ b/superset-frontend/plugins/plugin-chart-point-cluster-map/src/index.ts
@@ -28,31 +28,30 @@ import controlPanel from './controlPanel';
 
 const metadata = new ChartMetadata({
   category: t('Map'),
-  credits: ['https://www.mapbox.com/mapbox-gl-js/api/'],
+  credits: ['https://maplibre.org/'],
   description: '',
   exampleGallery: [
     { url: example1, urlDark: example1Dark, caption: t('Light mode') },
     { url: example2, urlDark: example2Dark, caption: t('Dark mode') },
   ],
-  name: t('MapBox'),
+  name: t('Point Cluster Map'),
   tags: [
     t('Business'),
     t('Intensity'),
-    t('Legacy'),
     t('Density'),
     t('Scatter'),
     t('Transformable'),
   ],
   thumbnail,
   thumbnailDark,
-  useLegacyApi: true,
 });
 
-export default class MapBoxChartPlugin extends ChartPlugin {
+export default class ScatterMapChartPlugin extends ChartPlugin {
   constructor() {
     super({
-      loadChart: () => import('./MapBox'),
+      loadChart: () => import('./MapLibre'),
       loadTransformProps: () => import('./transformProps'),
+      loadBuildQuery: () => import('./buildQuery'),
       metadata,
       controlPanel,
     });
diff --git a/superset-frontend/plugins/plugin-chart-point-cluster-map/src/stories/PointClusterMap.stories.tsx b/superset-frontend/plugins/plugin-chart-point-cluster-map/src/stories/PointClusterMap.stories.tsx
new file mode 100644
index 00000000000..d6c5eeae3f3
--- /dev/null
+++ b/superset-frontend/plugins/plugin-chart-point-cluster-map/src/stories/PointClusterMap.stories.tsx
@@ -0,0 +1,176 @@
+/**
+ * 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.
+ */
+
+/* eslint-disable sort-keys, no-magic-numbers */
+import { SuperChart } from '@superset-ui/core';
+import { useTheme } from '@apache-superset/core/theme';
+import ScatterMapChartPlugin from '@superset-ui/plugin-chart-point-cluster-map';
+import { withResizableChartDemo } from '@storybook-shared';
+import { generateData } from './data';
+
+new ScatterMapChartPlugin().configure({ key: 'point_cluster_map' }).register();
+
+export default {
+  title: 'Chart Plugins/plugin-chart-point-cluster-map',
+  decorators: [withResizableChartDemo],
+  args: {
+    clusteringRadius: 60,
+    globalOpacity: 1,
+    pointRadius: 'Auto',
+    renderWhileDragging: true,
+    mapRenderer: 'maplibre',
+  },
+  argTypes: {
+    clusteringRadius: {
+      control: { type: 'range', min: 0, max: 200, step: 10 },
+      description: 'Radius in pixels for clustering points',
+    },
+    globalOpacity: {
+      control: { type: 'range', min: 0, max: 1, step: 0.1 },
+      description: 'Opacity of map markers',
+    },
+    pointRadius: {
+      control: 'select',
+      options: ['Auto', 1, 2, 5, 10, 20, 50],
+      description: 'Size of point markers',
+    },
+    renderWhileDragging: {
+      control: 'boolean',
+      description: 'Render markers while dragging the map',
+    },
+    mapRenderer: {
+      control: 'select',
+      options: ['maplibre', 'mapbox'],
+      description:
+        'Map renderer. MapLibre is open-source. Mapbox requires MAPBOX_API_KEY.',
+    },
+  },
+};
+
+export const InteractiveSuperclusterMap = ({
+  clusteringRadius,
+  globalOpacity,
+  pointRadius,
+  renderWhileDragging,
+  mapRenderer,
+  width,
+  height,
+}: {
+  clusteringRadius: number;
+  globalOpacity: number;
+  pointRadius: string | number;
+  renderWhileDragging: boolean;
+  mapRenderer: string;
+  width: number;
+  height: number;
+}) => {
+  const theme = useTheme();
+  return (
+    <SuperChart
+      chartType="point_cluster_map"
+      width={width}
+      height={height}
+      queriesData={[{ data: generateData(theme) }]}
+      formData={{
+        clustering_radius: String(clusteringRadius),
+        global_opacity: globalOpacity,
+        map_color: '#008b8b',
+        map_label: [],
+        map_renderer: mapRenderer,
+        maplibre_style: 'https://tiles.openfreemap.org/styles/liberty',
+        mapbox_style: 'mapbox://styles/mapbox/light-v11',
+        pandas_aggfunc: 'sum',
+        point_radius: pointRadius,
+        point_radius_unit: 'Pixels',
+        render_while_dragging: renderWhileDragging,
+        viewport_latitude: 37.78,
+        viewport_longitude: -122.42,
+        viewport_zoom: 12,
+      }}
+    />
+  );
+};
+
+export const WithMetricLabels = ({
+  width,
+  height,
+}: {
+  width: number;
+  height: number;
+}) => {
+  const theme = useTheme();
+  return (
+    <SuperChart
+      chartType="point_cluster_map"
+      width={width}
+      height={height}
+      queriesData={[{ data: generateData(theme) }]}
+      formData={{
+        clustering_radius: '60',
+        global_opacity: 1,
+        map_color: '#dc143c',
+        map_label: ['metric'],
+        map_renderer: 'maplibre',
+        maplibre_style:
+          'https://basemaps.cartocdn.com/gl/dark-matter-gl-style/style.json',
+        pandas_aggfunc: 'sum',
+        point_radius: 'Auto',
+        point_radius_unit: 'Pixels',
+        render_while_dragging: true,
+        viewport_latitude: 37.78,
+        viewport_longitude: -122.42,
+        viewport_zoom: 12,
+      }}
+    />
+  );
+};
+
+export const NoClustering = ({
+  width,
+  height,
+}: {
+  width: number;
+  height: number;
+}) => {
+  const theme = useTheme();
+  return (
+    <SuperChart
+      chartType="point_cluster_map"
+      width={width}
+      height={height}
+      queriesData={[{ data: generateData(theme) }]}
+      formData={{
+        clustering_radius: '0',
+        global_opacity: 0.8,
+        map_color: '#228b22',
+        map_label: [],
+        map_renderer: 'maplibre',
+        maplibre_style:
+          'https://basemaps.cartocdn.com/gl/voyager-gl-style/style.json',
+        pandas_aggfunc: 'sum',
+        point_radius: 'Auto',
+        point_radius_unit: 'Pixels',
+        render_while_dragging: true,
+        viewport_latitude: 37.78,
+        viewport_longitude: -122.42,
+        viewport_zoom: 12,
+      }}
+    />
+  );
+};
diff --git a/superset-frontend/plugins/legacy-plugin-chart-map-box/src/stories/data.ts b/superset-frontend/plugins/plugin-chart-point-cluster-map/src/stories/data.ts
similarity index 100%
rename from superset-frontend/plugins/legacy-plugin-chart-map-box/src/stories/data.ts
rename to superset-frontend/plugins/plugin-chart-point-cluster-map/src/stories/data.ts
diff --git a/superset-frontend/plugins/plugin-chart-point-cluster-map/src/transformProps.ts b/superset-frontend/plugins/plugin-chart-point-cluster-map/src/transformProps.ts
new file mode 100644
index 00000000000..04d0cae20a8
--- /dev/null
+++ b/superset-frontend/plugins/plugin-chart-point-cluster-map/src/transformProps.ts
@@ -0,0 +1,292 @@
+/**
+ * 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 Supercluster, {
+  type Options as SuperclusterOptions,
+} from 'supercluster';
+import { ChartProps } from '@superset-ui/core';
+import { t } from '@apache-superset/core/translation';
+import { DEFAULT_POINT_RADIUS, DEFAULT_MAX_ZOOM } from './MapLibre';
+import roundDecimal from './utils/roundDecimal';
+
+const NOOP = () => {};
+
+// Geo precision to limit decimal places (matching legacy backend behavior)
+const GEO_PRECISION = 10;
+
+const MIN_LONGITUDE = -180;
+const MAX_LONGITUDE = 180;
+const MIN_LATITUDE = -90;
+const MAX_LATITUDE = 90;
+const MIN_ZOOM = 0;
+
+function toFiniteNumber(
+  value: string | number | null | undefined,
+): number | undefined {
+  if (value === null || value === undefined) return undefined;
+  const normalizedValue = typeof value === 'string' ? value.trim() : value;
+  if (normalizedValue === '') return undefined;
+  const num = Number(normalizedValue);
+  return Number.isFinite(num) ? num : undefined;
+}
+
+function clampNumber(
+  value: number | undefined,
+  min: number,
+  max: number,
+): number | undefined {
+  if (value === undefined) return undefined;
+  return Math.min(max, Math.max(min, value));
+}
+
+interface PointProperties {
+  metric: number | string | null;
+  radius: number | string | null;
+}
+
+interface ClusterProperties {
+  metric: number;
+  sum: number;
+  squaredSum: number;
+  min: number;
+  max: number;
+}
+
+interface DataRecord {
+  [key: string]: string | number | null | undefined;
+}
+
+function buildGeoJSONFromRecords(
+  records: DataRecord[],
+  lonCol: string,
+  latCol: string,
+  labelCol: string | null,
+  pointRadiusCol: string | null,
+) {
+  const features: GeoJSON.Feature<GeoJSON.Point, PointProperties>[] = [];
+  let minLon = Infinity;
+  let maxLon = -Infinity;
+  let minLat = Infinity;
+  let maxLat = -Infinity;
+
+  for (const record of records) {
+    const rawLon = record[lonCol];
+    const rawLat = record[latCol];
+    if (rawLon == null || rawLat == null) {
+      continue;
+    }
+    const lon = Number(rawLon);
+    const lat = Number(rawLat);
+    if (!Number.isFinite(lon) || !Number.isFinite(lat)) {
+      continue;
+    }
+
+    const roundedLon = roundDecimal(lon, GEO_PRECISION);
+    const roundedLat = roundDecimal(lat, GEO_PRECISION);
+
+    minLon = Math.min(minLon, roundedLon);
+    maxLon = Math.max(maxLon, roundedLon);
+    minLat = Math.min(minLat, roundedLat);
+    maxLat = Math.max(maxLat, roundedLat);
+
+    const metric = labelCol != null ? (record[labelCol] ?? null) : null;
+    const radius =
+      pointRadiusCol != null ? (record[pointRadiusCol] ?? null) : null;
+
+    features.push({
+      type: 'Feature',
+      properties: { metric, radius },
+      geometry: {
+        type: 'Point',
+        coordinates: [roundedLon, roundedLat],
+      },
+    });
+  }
+
+  const bounds: [[number, number], [number, number]] | undefined =
+    features.length > 0
+      ? [
+          [minLon, minLat],
+          [maxLon, maxLat],
+        ]
+      : undefined;
+
+  return {
+    geoJSON: { type: 'FeatureCollection' as const, features },
+    bounds,
+  };
+}
+
+export default function transformProps(chartProps: ChartProps) {
+  const {
+    width,
+    height,
+    rawFormData: formData,
+    hooks,
+    queriesData,
+  } = chartProps;
+  const { onError = NOOP, setControlValue = NOOP } = hooks;
+
+  const {
+    all_columns_x: allColumnsX,
+    all_columns_y: allColumnsY,
+    clustering_radius: clusteringRadius,
+    global_opacity: globalOpacity,
+    map_color: maplibreColor,
+    map_label: maplibreLabel,
+    map_renderer: mapProvider,
+    maplibre_style: maplibreStyle,
+    mapbox_style: mapboxStyle = '',
+    pandas_aggfunc: pandasAggfunc,
+    point_radius: pointRadius,
+    point_radius_unit: pointRadiusUnit,
+    render_while_dragging: renderWhileDragging,
+    viewport_longitude: viewportLongitude,
+    viewport_latitude: viewportLatitude,
+    viewport_zoom: viewportZoom,
+  } = formData;
+
+  // Support two data formats:
+  // 1. Legacy/GeoJSON: queriesData[0].data is an object with { geoJSON, bounds, hasCustomMetric }
+  // 2. Tabular records: queriesData[0].data is an array of flat records from a SQL query
+  const rawData = queriesData[0]?.data;
+  const isLegacyFormat = rawData && !Array.isArray(rawData) && rawData.geoJSON;
+
+  let geoJSON: { type: 'FeatureCollection'; features: any[] };
+  let bounds: [[number, number], [number, number]] | undefined;
+  let hasCustomMetric: boolean;
+
+  if (isLegacyFormat) {
+    const legacy = rawData as any;
+    ({ geoJSON } = legacy);
+    ({ bounds } = legacy);
+    hasCustomMetric = legacy.hasCustomMetric ?? false;
+  } else {
+    const records: DataRecord[] = (rawData as DataRecord[]) || [];
+    hasCustomMetric =
+      maplibreLabel != null &&
+      maplibreLabel.length > 0 &&
+      maplibreLabel[0] !== 'count';
+    const labelCol = hasCustomMetric ? maplibreLabel[0] : null;
+    const pointRadiusCol =
+      pointRadius && pointRadius !== 'Auto' ? pointRadius : null;
+
+    const built = buildGeoJSONFromRecords(
+      records,
+      allColumnsX,
+      allColumnsY,
+      labelCol,
+      pointRadiusCol,
+    );
+    ({ geoJSON } = built);
+    ({ bounds } = built);
+  }
+
+  // Validate color — supports hex (#rrggbb) and rgb(r, g, b) formats
+  let rgb: string[] | null = null;
+  const hexMatch = /^#([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})$/i.exec(
+    maplibreColor,
+  );
+  if (hexMatch) {
+    rgb = [
+      maplibreColor,
+      String(parseInt(hexMatch[1], 16)),
+      String(parseInt(hexMatch[2], 16)),
+      String(parseInt(hexMatch[3], 16)),
+    ];
+  } else {
+    rgb = /^rgb\((\d{1,3}),\s*(\d{1,3}),\s*(\d{1,3})\)$/.exec(maplibreColor);
+  }
+  if (rgb === null) {
+    onError(t("Color field must be a hex color (#rrggbb) or 'rgb(r, g, b)'"));
+    // Fall back to a safe default color so the chart can still render
+    rgb = ['', '0', '0', '0'];
+  }
+
+  const opts: SuperclusterOptions<PointProperties, ClusterProperties> = {
+    maxZoom: DEFAULT_MAX_ZOOM,
+    radius: clusteringRadius,
+  };
+  if (hasCustomMetric) {
+    opts.map = (prop: PointProperties) => ({
+      metric: Number(prop.metric) || 0,
+      sum: Number(prop.metric) || 0,
+      squaredSum: (Number(prop.metric) || 0) ** 2,
+      min: Number(prop.metric) || 0,
+      max: Number(prop.metric) || 0,
+    });
+    opts.reduce = (accu: ClusterProperties, prop: ClusterProperties) => {
+      /* eslint-disable no-param-reassign */
+      accu.sum += prop.sum;
+      accu.squaredSum += prop.squaredSum;
+      accu.min = Math.min(accu.min, prop.min);
+      accu.max = Math.max(accu.max, prop.max);
+      /* eslint-enable no-param-reassign */
+    };
+  }
+  const clusterer = new Supercluster<PointProperties, ClusterProperties>(opts);
+  // Disable strict typecheck on load since Supercluster typings have namespace issues with esModuleInterop
+  clusterer.load(geoJSON.features as any);
+
+  return {
+    width,
+    height,
+    aggregatorName: pandasAggfunc,
+    bounds,
+    clusterer,
+    globalOpacity: Math.min(1, Math.max(0, toFiniteNumber(globalOpacity) ?? 1)),
+    hasCustomMetric,
+    mapProvider,
+    mapStyle:
+      mapProvider === 'mapbox'
+        ? (mapboxStyle as string)
+        : (maplibreStyle as string),
+    onViewportChange({
+      latitude,
+      longitude,
+      zoom,
+    }: {
+      latitude: number;
+      longitude: number;
+      zoom: number;
+    }) {
+      setControlValue('viewport_longitude', longitude);
+      setControlValue('viewport_latitude', latitude);
+      setControlValue('viewport_zoom', zoom);
+    },
+    pointRadius: DEFAULT_POINT_RADIUS,
+    pointRadiusUnit,
+    renderWhileDragging,
+    rgb,
+    viewportLongitude: clampNumber(
+      toFiniteNumber(viewportLongitude),
+      MIN_LONGITUDE,
+      MAX_LONGITUDE,
+    ),
+    viewportLatitude: clampNumber(
+      toFiniteNumber(viewportLatitude),
+      MIN_LATITUDE,
+      MAX_LATITUDE,
+    ),
+    viewportZoom: clampNumber(
+      toFiniteNumber(viewportZoom),
+      MIN_ZOOM,
+      DEFAULT_MAX_ZOOM,
+    ),
+  };
+}
diff --git a/superset-frontend/plugins/legacy-plugin-chart-map-box/src/utils/geo.ts b/superset-frontend/plugins/plugin-chart-point-cluster-map/src/utils/geo.ts
similarity index 100%
rename from superset-frontend/plugins/legacy-plugin-chart-map-box/src/utils/geo.ts
rename to superset-frontend/plugins/plugin-chart-point-cluster-map/src/utils/geo.ts
diff --git a/superset-frontend/plugins/legacy-plugin-chart-map-box/src/utils/luminanceFromRGB.ts b/superset-frontend/plugins/plugin-chart-point-cluster-map/src/utils/luminanceFromRGB.ts
similarity index 100%
rename from superset-frontend/plugins/legacy-plugin-chart-map-box/src/utils/luminanceFromRGB.ts
rename to superset-frontend/plugins/plugin-chart-point-cluster-map/src/utils/luminanceFromRGB.ts
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/utils/colors.ts b/superset-frontend/plugins/plugin-chart-point-cluster-map/src/utils/mapbox.ts
similarity index 62%
copy from superset-frontend/plugins/legacy-preset-chart-deckgl/src/utils/colors.ts
copy to superset-frontend/plugins/plugin-chart-point-cluster-map/src/utils/mapbox.ts
index 7f562dbca23..b617b4c37cc 100644
--- a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/utils/colors.ts
+++ b/superset-frontend/plugins/plugin-chart-point-cluster-map/src/utils/mapbox.ts
@@ -16,17 +16,20 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-import { rgb } from 'd3-color';
 
-// eslint-disable-next-line import/prefer-default-export
-export function hexToRGB(
-  hex: string | undefined,
-  alpha = 255,
-): [number, number, number, number] {
-  if (!hex) {
-    return [0, 0, 0, alpha];
+export function getMapboxApiKey(): string {
+  if (typeof document === 'undefined') {
+    return '';
   }
-  const { r, g, b } = rgb(hex);
-
-  return [r, g, b, alpha];
+  try {
+    const appContainer = document.getElementById('app');
+    const dataBootstrap = appContainer?.getAttribute('data-bootstrap');
+    if (dataBootstrap) {
+      const bootstrapData = JSON.parse(dataBootstrap);
+      return bootstrapData?.common?.conf?.MAPBOX_API_KEY || '';
+    }
+  } catch {
+    // If bootstrap data is unavailable or malformed, return empty string
+  }
+  return '';
 }
diff --git a/superset-frontend/plugins/legacy-plugin-chart-map-box/src/utils/roundDecimal.ts b/superset-frontend/plugins/plugin-chart-point-cluster-map/src/utils/roundDecimal.ts
similarity index 100%
rename from superset-frontend/plugins/legacy-plugin-chart-map-box/src/utils/roundDecimal.ts
rename to superset-frontend/plugins/plugin-chart-point-cluster-map/src/utils/roundDecimal.ts
diff --git a/superset-frontend/plugins/plugin-chart-point-cluster-map/test/MapLibre.test.tsx b/superset-frontend/plugins/plugin-chart-point-cluster-map/test/MapLibre.test.tsx
new file mode 100644
index 00000000000..7c636e1bcd1
--- /dev/null
+++ b/superset-frontend/plugins/plugin-chart-point-cluster-map/test/MapLibre.test.tsx
@@ -0,0 +1,262 @@
+/*
+ * 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 { type ReactNode } from 'react';
+import { render } from '@testing-library/react';
+
+// Capture the most recent viewport props passed to the Map component
+let lastMapProps: Record<string, unknown> = {};
+const mockFitBounds = jest.fn();
+
+jest.mock('react-map-gl/maplibre', () => {
+  const MockMap = (props: Record<string, unknown>) => {
+    lastMapProps = props;
+    return <div data-testid="map-gl">{props.children as ReactNode}</div>;
+  };
+  return { __esModule: true, Map: MockMap };
+});
+
+jest.mock('react-map-gl/mapbox', () => {
+  const MockMap = (props: Record<string, unknown>) => {
+    lastMapProps = props;
+    return <div data-testid="map-gl">{props.children as ReactNode}</div>;
+  };
+  return { __esModule: true, Map: MockMap };
+});
+
+jest.mock('@math.gl/web-mercator', () => ({
+  WebMercatorViewport: jest
+    .fn()
+    .mockImplementation(
+      ({ width, height }: { width: number; height: number }) => ({
+        fitBounds: (bounds: [[number, number], [number, number]]) =>
+          mockFitBounds(bounds, width, height),
+      }),
+    ),
+}));
+
+jest.mock('../src/components/ScatterPlotOverlay', () => {
+  const MockOverlay = (props: Record<string, unknown>) => (
+    <div data-testid="scatter-overlay" data-opacity={props.globalOpacity} />
+  );
+  return { __esModule: true, default: MockOverlay };
+});
+
+jest.mock('@apache-superset/core/theme', () => ({
+  useTheme: () => ({ colorTextSecondary: '#666' }),
+}));
+
+jest.mock('maplibre-gl/dist/maplibre-gl.css', () => ({}));
+jest.mock('../src/MapLibre.css', () => ({}));
+
+// eslint-disable-next-line import/first
+import MapLibre from '../src/MapLibre';
+
+const defaultProps = {
+  width: 800,
+  height: 600,
+  clusterer: {
+    getClusters: jest.fn().mockReturnValue([]),
+  },
+  globalOpacity: 1,
+  mapProvider: 'maplibre',
+  mapStyle: 'https://tiles.openfreemap.org/styles/liberty',
+  pointRadius: 60,
+  pointRadiusUnit: 'Pixels',
+  renderWhileDragging: true,
+  rgb: ['', 255, 0, 0] as (string | number)[],
+  hasCustomMetric: false,
+  bounds: [
+    [-74.0, 40.7],
+    [-73.9, 40.8],
+  ] as [[number, number], [number, number]],
+  onViewportChange: jest.fn(),
+};
+
+beforeEach(() => {
+  lastMapProps = {};
+  jest.clearAllMocks();
+  mockFitBounds.mockImplementation(
+    (
+      bounds: [[number, number], [number, number]],
+      width: number,
+      height: number,
+    ) => ({
+      latitude: Number(((bounds[0][1] + bounds[1][1]) / 2).toFixed(2)),
+      longitude: Number(((bounds[0][0] + bounds[1][0]) / 2).toFixed(2)),
+      zoom: Number((10 + width / 1000 + height / 10000).toFixed(2)),
+    }),
+  );
+});
+
+test('initializes viewport from bounds', () => {
+  render(<MapLibre {...defaultProps} />);
+  expect(lastMapProps.longitude).toBe(-73.95);
+  expect(lastMapProps.latitude).toBe(40.75);
+  expect(lastMapProps.zoom).toBe(10.86);
+});
+
+test('initializes viewport from props when provided', () => {
+  render(
+    <MapLibre
+      {...defaultProps}
+      viewportLongitude={-122.4}
+      viewportLatitude={37.8}
+      viewportZoom={5}
+    />,
+  );
+  expect(lastMapProps.longitude).toBe(-122.4);
+  expect(lastMapProps.latitude).toBe(37.8);
+  expect(lastMapProps.zoom).toBe(5);
+});
+
+test('updates viewport when viewport props change', () => {
+  const { rerender } = render(
+    <MapLibre
+      {...defaultProps}
+      viewportLongitude={-73.95}
+      viewportLatitude={40.75}
+      viewportZoom={10}
+    />,
+  );
+
+  rerender(
+    <MapLibre
+      {...defaultProps}
+      viewportLongitude={-122.4}
+      viewportLatitude={37.8}
+      viewportZoom={5}
+    />,
+  );
+
+  expect(lastMapProps.longitude).toBe(-122.4);
+  expect(lastMapProps.latitude).toBe(37.8);
+  expect(lastMapProps.zoom).toBe(5);
+});
+
+test('does not loop when viewport state matches new props', () => {
+  const { rerender } = render(
+    <MapLibre
+      {...defaultProps}
+      viewportLongitude={-73.95}
+      viewportLatitude={40.75}
+      viewportZoom={10}
+    />,
+  );
+
+  rerender(
+    <MapLibre
+      {...defaultProps}
+      viewportLongitude={-73.95}
+      viewportLatitude={40.75}
+      viewportZoom={10}
+    />,
+  );
+
+  expect(lastMapProps.longitude).toBe(-73.95);
+  expect(lastMapProps.latitude).toBe(40.75);
+  expect(lastMapProps.zoom).toBe(10);
+});
+
+test('passes globalOpacity to ScatterPlotOverlay', () => {
+  const { container } = render(
+    <MapLibre {...defaultProps} globalOpacity={0.5} />,
+  );
+  const overlay = container.querySelector('[data-testid="scatter-overlay"]');
+  expect(overlay).not.toBeNull();
+  expect(overlay!.getAttribute('data-opacity')).toBe('0.5');
+});
+
+test('handles undefined bounds gracefully', () => {
+  render(<MapLibre {...defaultProps} bounds={undefined} />);
+  expect(lastMapProps.longitude).toBe(0);
+  expect(lastMapProps.latitude).toBe(0);
+  expect(lastMapProps.zoom).toBe(1);
+});
+
+test('applies partial viewport props on update', () => {
+  const { rerender } = render(<MapLibre {...defaultProps} />);
+
+  rerender(<MapLibre {...defaultProps} viewportLongitude={-122.4} />);
+
+  expect(lastMapProps.longitude).toBe(-122.4);
+  // lat and zoom come from fitBounds
+  expect(lastMapProps.latitude).toBe(40.75);
+  expect(lastMapProps.zoom).toBe(10.86);
+});
+
+test('restores fitBounds when viewport props are cleared', () => {
+  const { rerender } = render(
+    <MapLibre
+      {...defaultProps}
+      viewportLongitude={-122.4}
+      viewportLatitude={37.8}
+      viewportZoom={5}
+    />,
+  );
+
+  // Clear all viewport props
+  rerender(<MapLibre {...defaultProps} />);
+
+  // Should revert to fitBounds values
+  expect(lastMapProps.longitude).toBe(-73.95);
+  expect(lastMapProps.latitude).toBe(40.75);
+  expect(lastMapProps.zoom).toBe(10.86);
+});
+
+test('restores only cleared viewport props, keeps the rest', () => {
+  const { rerender } = render(
+    <MapLibre
+      {...defaultProps}
+      viewportLongitude={-122.4}
+      viewportLatitude={37.8}
+      viewportZoom={5}
+    />,
+  );
+
+  // Clear only longitude, keep lat/zoom
+  rerender(
+    <MapLibre {...defaultProps} viewportLatitude={37.8} viewportZoom={5} />,
+  );
+
+  // Longitude reverts to fitBounds, lat/zoom stay
+  expect(lastMapProps.longitude).toBe(-73.95);
+  expect(lastMapProps.latitude).toBe(37.8);
+  expect(lastMapProps.zoom).toBe(5);
+});
+
+test('falls back to default viewport when cleared with undefined bounds', () => {
+  const { rerender } = render(
+    <MapLibre
+      {...defaultProps}
+      bounds={undefined}
+      viewportLongitude={-122.4}
+      viewportLatitude={37.8}
+      viewportZoom={5}
+    />,
+  );
+
+  // Clear viewport props — no bounds to fitBounds to
+  rerender(<MapLibre {...defaultProps} bounds={undefined} />);
+
+  // Should fall back to {0, 0, 1}
+  expect(lastMapProps.longitude).toBe(0);
+  expect(lastMapProps.latitude).toBe(0);
+  expect(lastMapProps.zoom).toBe(1);
+});
diff --git a/superset-frontend/plugins/legacy-plugin-chart-map-box/test/ScatterPlotGlowOverlay.test.tsx b/superset-frontend/plugins/plugin-chart-point-cluster-map/test/ScatterPlotOverlay.test.tsx
similarity index 92%
rename from superset-frontend/plugins/legacy-plugin-chart-map-box/test/ScatterPlotGlowOverlay.test.tsx
rename to superset-frontend/plugins/plugin-chart-point-cluster-map/test/ScatterPlotOverlay.test.tsx
index 8ce96810548..cafa91aaeba 100644
--- a/superset-frontend/plugins/legacy-plugin-chart-map-box/test/ScatterPlotGlowOverlay.test.tsx
+++ b/superset-frontend/plugins/plugin-chart-point-cluster-map/test/ScatterPlotOverlay.test.tsx
@@ -18,7 +18,11 @@
  */
 
 import { render } from '@testing-library/react';
-import ScatterPlotGlowOverlay from '../src/ScatterPlotGlowOverlay';
+import ScatterPlotOverlay from '../src/components/ScatterPlotOverlay';
+import {
+  MIN_CLUSTER_RADIUS_RATIO,
+  MAX_POINT_RADIUS_RATIO,
+} from '../src/components/ScatterPlotOverlay';
 
 type MockGradient = {
   addColorStop: jest.Mock<void, [number, string]>;
@@ -67,22 +71,20 @@ declare global {
   var mockRedraw: unknown;
 }
 
-// Mock react-map-gl's CanvasOverlay
-jest.mock('react-map-gl', () => ({
-  CanvasOverlay: ({ redraw }: { redraw: unknown }) => {
-    // Store the redraw function so tests can call it
+// Mock the CanvasOverlay component to capture the redraw function
+jest.mock('../src/components/CanvasOverlay', () => ({
+  __esModule: true,
+  default: ({ redraw }: { redraw: unknown }) => {
     global.mockRedraw = redraw;
     return <div data-testid="canvas-overlay" />;
   },
 }));
 
-// Mock utility functions
 jest.mock('../src/utils/luminanceFromRGB', () => ({
   __esModule: true,
-  default: jest.fn(() => 150), // Return a value above the dark threshold
+  default: jest.fn(() => 150),
 }));
 
-// Test helpers
 const createMockCanvas = () => {
   const ctx: MockCanvasContext = {
     clearRect: jest.fn(),
@@ -151,8 +153,10 @@ const defaultProps = {
   rgb: ['', 255, 0, 0] as [string, number, number, number],
   globalOpacity: 1,
 };
-const MIN_VISIBLE_POINT_RADIUS = 10;
-const MAX_VISIBLE_POINT_RADIUS = 20;
+const MIN_VISIBLE_POINT_RADIUS =
+  defaultProps.dotRadius * MIN_CLUSTER_RADIUS_RATIO;
+const MAX_VISIBLE_POINT_RADIUS =
+  defaultProps.dotRadius * MAX_POINT_RADIUS_RATIO;
 
 test('renders map with varying radius values in Pixels mode', () => {
   const locations = [
@@ -162,7 +166,7 @@ test('renders map with varying radius values in Pixels mode', () => {
   ];
 
   render(
-    <ScatterPlotGlowOverlay
+    <ScatterPlotOverlay
       {...defaultProps}
       locations={locations}
       pointRadiusUnit="Pixels"
@@ -172,13 +176,11 @@ test('renders map with varying radius values in Pixels mode', () => {
 
   const arcCalls = redrawParams.ctx.arc.mock.calls;
 
-  // With dotRadius=60, pixel-sized points should map to the visible 10-20 range.
   arcCalls.forEach(call => {
     expect(call[2]).toBeGreaterThanOrEqual(MIN_VISIBLE_POINT_RADIUS);
     expect(call[2]).toBeLessThanOrEqual(MAX_VISIBLE_POINT_RADIUS);
   });
 
-  // Ordering should be preserved: radius 10 < 50 < 100
   expect(arcCalls[0][2]).toBeLessThan(arcCalls[1][2]);
   expect(arcCalls[1][2]).toBeLessThan(arcCalls[2][2]);
 });
@@ -192,7 +194,7 @@ test('handles dataset with uniform radius values', () => {
 
   expect(() => {
     render(
-      <ScatterPlotGlowOverlay
+      <ScatterPlotOverlay
         {...defaultProps}
         locations={locations}
         pointRadiusUnit="Pixels"
@@ -211,7 +213,7 @@ test('renders successfully when data contains non-finite values', () => {
 
   expect(() => {
     render(
-      <ScatterPlotGlowOverlay
+      <ScatterPlotOverlay
         {...defaultProps}
         locations={locations}
         pointRadiusUnit="Pixels"
@@ -229,7 +231,7 @@ test('handles radius values provided as strings', () => {
   ];
 
   render(
-    <ScatterPlotGlowOverlay
+    <ScatterPlotOverlay
       {...defaultProps}
       locations={locations}
       pointRadiusUnit="Pixels"
@@ -256,7 +258,7 @@ test('treats blank radius strings as missing values', () => {
   ];
 
   render(
-    <ScatterPlotGlowOverlay
+    <ScatterPlotOverlay
       {...defaultProps}
       locations={locations}
       pointRadiusUnit="Pixels"
@@ -280,7 +282,7 @@ test('renders points when radius values are missing', () => {
 
   expect(() => {
     render(
-      <ScatterPlotGlowOverlay
+      <ScatterPlotOverlay
         {...defaultProps}
         locations={locations}
         pointRadiusUnit="Pixels"
@@ -304,7 +306,7 @@ test('renders both cluster and non-cluster points correctly', () => {
 
   expect(() => {
     render(
-      <ScatterPlotGlowOverlay
+      <ScatterPlotOverlay
         {...defaultProps}
         locations={locations}
         pointRadiusUnit="Pixels"
@@ -323,7 +325,7 @@ test('renders map with multiple points with different radius values', () => {
 
   expect(() => {
     render(
-      <ScatterPlotGlowOverlay
+      <ScatterPlotOverlay
         {...defaultProps}
         locations={locations}
         pointRadiusUnit="Pixels"
@@ -341,7 +343,7 @@ test('renders map with Kilometers mode', () => {
 
   expect(() => {
     render(
-      <ScatterPlotGlowOverlay
+      <ScatterPlotOverlay
         {...defaultProps}
         locations={locations}
         pointRadiusUnit="Kilometers"
@@ -360,7 +362,7 @@ test('renders map with Miles mode', () => {
 
   expect(() => {
     render(
-      <ScatterPlotGlowOverlay
+      <ScatterPlotOverlay
         {...defaultProps}
         locations={locations}
         pointRadiusUnit="Miles"
@@ -378,7 +380,7 @@ test('displays metric property labels on points', () => {
 
   expect(() => {
     render(
-      <ScatterPlotGlowOverlay
+      <ScatterPlotOverlay
         {...defaultProps}
         locations={locations}
         pointRadiusUnit="Pixels"
@@ -391,7 +393,7 @@ test('displays metric property labels on points', () => {
 test('handles empty dataset without errors', () => {
   expect(() => {
     render(
-      <ScatterPlotGlowOverlay
+      <ScatterPlotOverlay
         {...defaultProps}
         locations={[]}
         pointRadiusUnit="Pixels"
@@ -410,7 +412,7 @@ test('handles extreme outlier radius values without breaking', () => {
 
   expect(() => {
     render(
-      <ScatterPlotGlowOverlay
+      <ScatterPlotOverlay
         {...defaultProps}
         locations={locations}
         pointRadiusUnit="Pixels"
@@ -431,7 +433,7 @@ test('renders successfully with mixed extreme and negative radius values', () =>
 
   expect(() => {
     render(
-      <ScatterPlotGlowOverlay
+      <ScatterPlotOverlay
         {...defaultProps}
         locations={locations}
         pointRadiusUnit="Pixels"
@@ -456,7 +458,7 @@ test('cluster radius is always >= max individual point radius in Pixels mode', (
   ];
 
   render(
-    <ScatterPlotGlowOverlay
+    <ScatterPlotOverlay
       {...defaultProps}
       locations={locations}
       aggregation="sum"
@@ -467,9 +469,7 @@ test('cluster radius is always >= max individual point radius in Pixels mode', (
 
   const arcCalls = redrawParams.ctx.arc.mock.calls;
 
-  // cluster with label=1 (index 0) should not be smaller than the largest point bubble
   expect(arcCalls[0][2]).toBeGreaterThanOrEqual(MAX_VISIBLE_POINT_RADIUS);
-  // point radii span the configured pixel range
   expect(arcCalls[1][2]).toBe(MIN_VISIBLE_POINT_RADIUS);
   expect(arcCalls[2][2]).toBe(MAX_VISIBLE_POINT_RADIUS);
   expect(arcCalls[0][2]).toBeGreaterThanOrEqual(arcCalls[2][2]);
@@ -490,7 +490,7 @@ test('largest cluster gets full dotRadius', () => {
   ];
 
   render(
-    <ScatterPlotGlowOverlay
+    <ScatterPlotOverlay
       {...defaultProps}
       locations={locations}
       aggregation="sum"
@@ -500,7 +500,6 @@ test('largest cluster gets full dotRadius', () => {
   const redrawParams = triggerRedraw();
 
   const arcCalls = redrawParams.ctx.arc.mock.calls;
-  // The largest cluster (label=100, maxLabel=100) should get full radius
   expect(arcCalls[1][2]).toBe(defaultProps.dotRadius);
 });
 
@@ -524,7 +523,7 @@ test('cluster radii preserve proportional ordering', () => {
   ];
 
   render(
-    <ScatterPlotGlowOverlay
+    <ScatterPlotOverlay
       {...defaultProps}
       locations={locations}
       aggregation="sum"
@@ -552,7 +551,7 @@ test('negative cluster label produces valid finite radius', () => {
   ];
 
   render(
-    <ScatterPlotGlowOverlay
+    <ScatterPlotOverlay
       {...defaultProps}
       locations={locations}
       aggregation="sum"
@@ -581,7 +580,7 @@ test('ignores non-finite cluster labels when computing cluster scaling bounds',
   ];
 
   render(
-    <ScatterPlotGlowOverlay
+    <ScatterPlotOverlay
       {...defaultProps}
       locations={locations}
       aggregation="sum"
@@ -606,7 +605,7 @@ test('single cluster with small maxLabel gets full dotRadius', () => {
   ];
 
   render(
-    <ScatterPlotGlowOverlay
+    <ScatterPlotOverlay
       {...defaultProps}
       locations={locations}
       aggregation="sum"
@@ -615,7 +614,6 @@ test('single cluster with small maxLabel gets full dotRadius', () => {
   const redrawParams = triggerRedraw();
 
   const arcCalls = redrawParams.ctx.arc.mock.calls;
-  // When there's only one cluster, label=maxLabel, so it gets full radius
   expect(arcCalls[0][2]).toBe(defaultProps.dotRadius);
 });
 
@@ -639,7 +637,7 @@ test('all-negative cluster labels produce differentiated radii by magnitude', ()
   ];
 
   render(
-    <ScatterPlotGlowOverlay
+    <ScatterPlotOverlay
       {...defaultProps}
       locations={locations}
       aggregation="sum"
@@ -652,7 +650,6 @@ test('all-negative cluster labels produce differentiated radii by magnitude', ()
   const rNeg10 = arcCalls[1][2];
   const rNeg1 = arcCalls[2][2];
 
-  // Higher magnitude = bigger circle: |-100| > |-10| > |-1|
   expect(rNeg1).toBeLessThan(rNeg10);
   expect(rNeg10).toBeLessThan(rNeg100);
   expect(Number.isFinite(rNeg100)).toBe(true);
@@ -682,7 +679,7 @@ test('mixed positive-and-negative cluster labels size by magnitude', () => {
   ];
 
   render(
-    <ScatterPlotGlowOverlay
+    <ScatterPlotOverlay
       {...defaultProps}
       locations={locations}
       aggregation="sum"
@@ -695,7 +692,6 @@ test('mixed positive-and-negative cluster labels size by magnitude', () => {
   const rZero = arcCalls[1][2];
   const r100 = arcCalls[2][2];
 
-  // Magnitude ordering: |0| < |-50| < |100|
   expect(rZero).toBeLessThan(rNeg50);
   expect(rNeg50).toBeLessThan(r100);
   expect(rZero).toBeGreaterThanOrEqual(MIN_VISIBLE_POINT_RADIUS);
@@ -722,7 +718,7 @@ test('all-identical negative labels get equal full radii', () => {
   ];
 
   render(
-    <ScatterPlotGlowOverlay
+    <ScatterPlotOverlay
       {...defaultProps}
       locations={locations}
       aggregation="sum"
@@ -750,7 +746,7 @@ test('single negative cluster gets full radius', () => {
   ];
 
   render(
-    <ScatterPlotGlowOverlay
+    <ScatterPlotOverlay
       {...defaultProps}
       locations={locations}
       aggregation="sum"
@@ -772,7 +768,7 @@ test('large negative cluster labels are abbreviated', () => {
   ];
 
   render(
-    <ScatterPlotGlowOverlay
+    <ScatterPlotOverlay
       {...defaultProps}
       locations={locations}
       aggregation="sum"
@@ -812,7 +808,7 @@ test.each([
     ];
 
     render(
-      <ScatterPlotGlowOverlay
+      <ScatterPlotOverlay
         {...defaultProps}
         locations={locations}
         aggregation={aggregation}
@@ -846,7 +842,7 @@ test('zero-value cluster is visible with minimum radius', () => {
   ];
 
   render(
-    <ScatterPlotGlowOverlay
+    <ScatterPlotOverlay
       {...defaultProps}
       locations={locations}
       aggregation="sum"
@@ -877,7 +873,7 @@ test('all-zero clusters use a finite radius', () => {
   ];
 
   render(
-    <ScatterPlotGlowOverlay
+    <ScatterPlotOverlay
       {...defaultProps}
       locations={locations}
       aggregation="sum"
diff --git a/superset-frontend/plugins/legacy-plugin-chart-map-box/test/controlPanel.test.ts b/superset-frontend/plugins/plugin-chart-point-cluster-map/test/controlPanel.test.ts
similarity index 100%
rename from superset-frontend/plugins/legacy-plugin-chart-map-box/test/controlPanel.test.ts
rename to superset-frontend/plugins/plugin-chart-point-cluster-map/test/controlPanel.test.ts
diff --git a/superset-frontend/plugins/legacy-plugin-chart-map-box/test/transformProps.test.ts b/superset-frontend/plugins/plugin-chart-point-cluster-map/test/transformProps.test.ts
similarity index 75%
rename from superset-frontend/plugins/legacy-plugin-chart-map-box/test/transformProps.test.ts
rename to superset-frontend/plugins/plugin-chart-point-cluster-map/test/transformProps.test.ts
index 483f64963dc..dc5a460c5f9 100644
--- a/superset-frontend/plugins/legacy-plugin-chart-map-box/test/transformProps.test.ts
+++ b/superset-frontend/plugins/plugin-chart-point-cluster-map/test/transformProps.test.ts
@@ -42,19 +42,23 @@ type TransformPropsResult = {
   viewportLongitude?: number;
   viewportLatitude?: number;
   viewportZoom?: number;
+  rgb?: string[] | null;
 };
 
 const baseFormData = {
-  clusteringRadius: 60,
-  globalOpacity: 0.8,
-  mapboxColor: 'rgb(0, 139, 139)',
-  mapboxStyle: 'mapbox://styles/mapbox/light-v9',
-  pandasAggfunc: 'sum',
-  pointRadiusUnit: 'Pixels',
-  renderWhileDragging: true,
-  viewportLongitude: -73.935242,
-  viewportLatitude: 40.73061,
-  viewportZoom: 9,
+  all_columns_x: 'lon',
+  all_columns_y: 'lat',
+  clustering_radius: 60,
+  global_opacity: 0.8,
+  map_color: 'rgb(0, 139, 139)',
+  map_renderer: 'maplibre',
+  maplibre_style: 'https://tiles.openfreemap.org/styles/liberty',
+  pandas_aggfunc: 'sum',
+  point_radius_unit: 'Pixels',
+  render_while_dragging: true,
+  viewport_longitude: -73.935242,
+  viewport_latitude: 40.73061,
+  viewport_zoom: 9,
 };
 
 const baseQueriesData = [
@@ -66,7 +70,6 @@ const baseQueriesData = [
       ] as [[number, number], [number, number]],
       geoJSON: { features: [] },
       hasCustomMetric: false,
-      mapboxApiKey: 'test-api-key',
     },
   },
 ];
@@ -88,15 +91,15 @@ function getTransformPropsResult(
 }
 
 test('extracts globalOpacity from formData', () => {
-  const result = getTransformPropsResult({ globalOpacity: 0.5 });
+  const result = getTransformPropsResult({ global_opacity: 0.5 });
   expect(result.globalOpacity).toBe(0.5);
 });
 
 test('extracts viewport values from formData', () => {
   const result = getTransformPropsResult({
-    viewportLongitude: -122.4,
-    viewportLatitude: 37.8,
-    viewportZoom: 12,
+    viewport_longitude: -122.4,
+    viewport_latitude: 37.8,
+    viewport_zoom: 12,
   });
   expect(result).toEqual(
     expect.objectContaining({
@@ -109,9 +112,9 @@ test('extracts viewport values from formData', () => {
 
 test('clamps viewport values to safe map ranges', () => {
   const result = getTransformPropsResult({
-    viewportLongitude: 190,
-    viewportLatitude: -100,
-    viewportZoom: 99,
+    viewport_longitude: 190,
+    viewport_latitude: -100,
+    viewport_zoom: 99,
   });
   expect(result).toEqual(
     expect.objectContaining({
@@ -148,9 +151,9 @@ test('provides onViewportChange callback that updates control values', () => {
 
 test('normalizes string viewport values to numbers', () => {
   const result = getTransformPropsResult({
-    viewportLongitude: '-122.4',
-    viewportLatitude: '37.8',
-    viewportZoom: '12',
+    viewport_longitude: '-122.4',
+    viewport_latitude: '37.8',
+    viewport_zoom: '12',
   });
   expect(result.viewportLongitude).toBe(-122.4);
   expect(result.viewportLatitude).toBe(37.8);
@@ -159,9 +162,9 @@ test('normalizes string viewport values to numbers', () => {
 
 test('normalizes empty viewport values to undefined', () => {
   const result = getTransformPropsResult({
-    viewportLongitude: '',
-    viewportLatitude: '',
-    viewportZoom: '',
+    viewport_longitude: '',
+    viewport_latitude: '',
+    viewport_zoom: '',
   });
   expect(result.viewportLongitude).toBeUndefined();
   expect(result.viewportLatitude).toBeUndefined();
@@ -170,9 +173,9 @@ test('normalizes empty viewport values to undefined', () => {
 
 test('normalizes whitespace-only viewport values to undefined', () => {
   const result = getTransformPropsResult({
-    viewportLongitude: '   ',
-    viewportLatitude: '\t',
-    viewportZoom: ' \n ',
+    viewport_longitude: '   ',
+    viewport_latitude: '\t',
+    viewport_zoom: ' \n ',
   });
   expect(result.viewportLongitude).toBeUndefined();
   expect(result.viewportLatitude).toBeUndefined();
@@ -180,31 +183,31 @@ test('normalizes whitespace-only viewport values to undefined', () => {
 });
 
 test('normalizes string opacity to number', () => {
-  const result = getTransformPropsResult({ globalOpacity: '0.5' });
+  const result = getTransformPropsResult({ global_opacity: '0.5' });
   expect(result.globalOpacity).toBe(0.5);
 });
 
 test('defaults empty opacity to 1', () => {
-  const result = getTransformPropsResult({ globalOpacity: '' });
+  const result = getTransformPropsResult({ global_opacity: '' });
   expect(result.globalOpacity).toBe(1);
 });
 
 test('defaults whitespace-only opacity to 1', () => {
-  const result = getTransformPropsResult({ globalOpacity: '   ' });
+  const result = getTransformPropsResult({ global_opacity: '   ' });
   expect(result.globalOpacity).toBe(1);
 });
 
 test('clamps opacity to [0, 1] range', () => {
-  expect(getTransformPropsResult({ globalOpacity: 5 }).globalOpacity).toBe(1);
-  expect(getTransformPropsResult({ globalOpacity: -1 }).globalOpacity).toBe(0);
+  expect(getTransformPropsResult({ global_opacity: 5 }).globalOpacity).toBe(1);
+  expect(getTransformPropsResult({ global_opacity: -1 }).globalOpacity).toBe(0);
 });
 
 test('passes through numeric values unchanged', () => {
   const result = getTransformPropsResult({
-    viewportLongitude: -122.4,
-    viewportLatitude: 37.8,
-    viewportZoom: 12,
-    globalOpacity: 0.8,
+    viewport_longitude: -122.4,
+    viewport_latitude: 37.8,
+    viewport_zoom: 12,
+    global_opacity: 0.8,
   });
   expect(result.viewportLongitude).toBe(-122.4);
   expect(result.viewportLatitude).toBe(37.8);
@@ -212,19 +215,18 @@ test('passes through numeric values unchanged', () => {
   expect(result.globalOpacity).toBe(0.8);
 });
 
-test('calls onError and returns empty object for invalid color', () => {
+test('calls onError and falls back to black for invalid color', () => {
   const onError = jest.fn();
   const chartProps = new ChartProps({
-    formData: { ...baseFormData, mapboxColor: 'invalid-color' },
+    formData: { ...baseFormData, map_color: 'invalid-color' },
     width: 800,
     height: 600,
     queriesData: baseQueriesData,
     hooks: { onError },
     theme: supersetTheme,
   });
-  const result = transformProps(chartProps);
-  expect(onError).toHaveBeenCalledWith(
-    "Color field must be of form 'rgb(%d, %d, %d)'",
-  );
-  expect(result).toEqual({});
+  const result = transformProps(chartProps) as TransformPropsResult;
+  expect(onError).toHaveBeenCalled();
+  // Falls back to black instead of returning empty object
+  expect(result.rgb).toEqual(['', '0', '0', '0']);
 });
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/tsconfig.json b/superset-frontend/plugins/plugin-chart-point-cluster-map/tsconfig.json
similarity index 58%
copy from superset-frontend/plugins/legacy-preset-chart-deckgl/tsconfig.json
copy to superset-frontend/plugins/plugin-chart-point-cluster-map/tsconfig.json
index e73ca5d4f87..1abdcd42c58 100644
--- a/superset-frontend/plugins/legacy-preset-chart-deckgl/tsconfig.json
+++ b/superset-frontend/plugins/plugin-chart-point-cluster-map/tsconfig.json
@@ -1,12 +1,7 @@
 {
   "extends": "../../tsconfig.json",
   "compilerOptions": {
-    // Path Resolution: Override baseUrl to maintain correct path mappings from parent config
-    // (e.g., "@apache-superset/core" -> "./packages/superset-core/src")
     "baseUrl": "../..",
-
-    // Directory Overrides: Parent config paths are relative to frontend root,
-    // but packages need paths relative to their own directory
     "outDir": "lib",
     "rootDir": "src",
     "declarationDir": "lib"
diff --git a/superset-frontend/plugins/legacy-plugin-chart-map-box/src/MapBox.css b/superset-frontend/plugins/plugin-chart-point-cluster-map/types/external.d.ts
similarity index 84%
copy from superset-frontend/plugins/legacy-plugin-chart-map-box/src/MapBox.css
copy to superset-frontend/plugins/plugin-chart-point-cluster-map/types/external.d.ts
index adade29f589..b904c352de4 100644
--- a/superset-frontend/plugins/legacy-plugin-chart-map-box/src/MapBox.css
+++ b/superset-frontend/plugins/plugin-chart-point-cluster-map/types/external.d.ts
@@ -1,4 +1,4 @@
-/**
+/*
  * 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
@@ -16,6 +16,13 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-.mapbox .slice_container div {
-  padding-top: 0px;
+
+declare module '*.png' {
+  const value: string;
+  export default value;
+}
+
+declare module '*.jpg' {
+  const value: string;
+  export default value;
 }
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/package.json b/superset-frontend/plugins/preset-chart-deckgl/package.json
similarity index 76%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/package.json
rename to superset-frontend/plugins/preset-chart-deckgl/package.json
index 64be5b9162b..60570314f37 100644
--- a/superset-frontend/plugins/legacy-preset-chart-deckgl/package.json
+++ b/superset-frontend/plugins/preset-chart-deckgl/package.json
@@ -1,7 +1,7 @@
 {
-  "name": "@superset-ui/legacy-preset-chart-deckgl",
-  "version": "0.20.4",
-  "description": "Superset Legacy Chart - deck.gl",
+  "name": "@superset-ui/preset-chart-deckgl",
+  "version": "1.0.0",
+  "description": "Superset Chart Plugin - deck.gl (MapLibre)",
   "keywords": [
     "superset"
   ],
@@ -12,7 +12,7 @@
   "repository": {
     "type": "git",
     "url": "https://github.com/apache/superset.git",
-    "directory": "superset-frontend/packages/legacy-preset-chart-deckgl"
+    "directory": "superset-frontend/plugins/preset-chart-deckgl"
   },
   "license": "Apache-2.0",
   "author": "Superset",
@@ -29,14 +29,13 @@
     "@deck.gl/extensions": "~9.2.9",
     "@deck.gl/geo-layers": "~9.2.5",
     "@deck.gl/layers": "~9.2.5",
+    "@deck.gl/mapbox": "~9.2.5",
     "@deck.gl/mesh-layers": "~9.2.5",
-    "@deck.gl/react": "~9.2.11",
     "@luma.gl/constants": "~9.2.5",
     "@luma.gl/core": "~9.2.5",
     "@luma.gl/engine": "~9.2.6",
     "@luma.gl/shadertools": "~9.2.6",
     "@luma.gl/webgl": "~9.2.6",
-    "@mapbox/tiny-sdf": "^2.0.7",
     "@mapbox/geojson-extent": "^1.0.1",
     "@math.gl/web-mercator": "^4.1.0",
     "@types/d3-array": "^3.2.2",
@@ -47,10 +46,12 @@
     "d3-scale": "^4.0.2",
     "handlebars": "^4.7.9",
     "lodash": "^4.18.1",
+    "maplibre-gl": "^5.0.0",
     "mousetrap": "^1.6.5",
     "ngeohash": "^0.6.3",
     "prop-types": "^15.8.1",
-    "underscore": "^1.13.8",
+    "react-map-gl": "^8.0.0",
+    "underscore": "^1.13.7",
     "urijs": "^1.19.11",
     "xss": "^1.0.15"
   },
@@ -65,10 +66,14 @@
     "@superset-ui/chart-controls": "*",
     "@superset-ui/core": "*",
     "dayjs": "^1.11.19",
-    "mapbox-gl": "*",
-    "react": "^17.0.2",
-    "react-dom": "^17.0.2",
-    "react-map-gl": "^6.1.19"
+    "mapbox-gl": ">=1.0.0",
+    "react": "^17.0.2 || ^19.0.0",
+    "react-dom": "^17.0.2 || ^19.0.0"
+  },
+  "peerDependenciesMeta": {
+    "mapbox-gl": {
+      "optional": true
+    }
   },
   "publishConfig": {
     "access": "public"
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/CategoricalDeckGLContainer.tsx b/superset-frontend/plugins/preset-chart-deckgl/src/CategoricalDeckGLContainer.tsx
similarity index 95%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/CategoricalDeckGLContainer.tsx
rename to superset-frontend/plugins/preset-chart-deckgl/src/CategoricalDeckGLContainer.tsx
index e032aefe282..afba0113056 100644
--- a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/CategoricalDeckGLContainer.tsx
+++ b/superset-frontend/plugins/preset-chart-deckgl/src/CategoricalDeckGLContainer.tsx
@@ -38,6 +38,7 @@ import {
 import type { Layer } from '@deck.gl/core';
 import Legend from './components/Legend';
 import { hexToRGB } from './utils/colors';
+import { getMapboxApiKey } from './utils/mapbox';
 import sandboxedEval from './utils/sandbox';
 import fitViewport, { Viewport } from './utils/fitViewport';
 import {
@@ -83,7 +84,6 @@ function getCategories(fd: QueryFormData, data: JsonObject[]) {
 export type CategoricalDeckGLContainerProps = {
   datasource: Datasource;
   formData: QueryFormData;
-  mapboxApiKey: string;
   getPoints: (data: JsonObject[]) => Point[];
   height: number;
   width: number;
@@ -155,7 +155,7 @@ const CategoricalDeckGLContainer = (props: CategoricalDeckGLContainerProps) => {
 
       switch (selectedColorScheme) {
         case COLOR_SCHEME_TYPES.fixed_color: {
-          color = fd.color_picker || { r: 0, g: 0, b: 0, a: 100 };
+          color = fd.color_picker || { r: 0, g: 0, b: 0, a: 1 };
           const colorArray = [color.r, color.g, color.b, color.a * 255];
 
           return data.map(d => ({ ...d, color: colorArray }));
@@ -166,7 +166,7 @@ const CategoricalDeckGLContainer = (props: CategoricalDeckGLContainerProps) => {
               r: 0,
               g: 0,
               b: 0,
-              a: 100,
+              a: 1,
             };
             const colorArray = [
               fallbackColor.r,
@@ -325,8 +325,15 @@ const CategoricalDeckGLContainer = (props: CategoricalDeckGLContainerProps) => {
         viewport={viewport}
         layers={getLayers()}
         setControlValue={props.setControlValue}
-        mapStyle={props.formData.mapbox_style}
-        mapboxApiAccessToken={props.mapboxApiKey}
+        mapStyle={
+          props.formData.map_renderer === 'mapbox'
+            ? props.formData.mapbox_style
+            : props.formData.maplibre_style
+        }
+        mapProvider={
+          props.formData.map_renderer === 'mapbox' ? 'mapbox' : 'maplibre'
+        }
+        mapboxApiKey={getMapboxApiKey()}
         width={props.width}
         height={props.height}
       />
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/DeckGLContainer.tsx b/superset-frontend/plugins/preset-chart-deckgl/src/DeckGLContainer.tsx
similarity index 64%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/DeckGLContainer.tsx
rename to superset-frontend/plugins/preset-chart-deckgl/src/DeckGLContainer.tsx
index d558532298d..c710d3a72d5 100644
--- a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/DeckGLContainer.tsx
+++ b/superset-frontend/plugins/preset-chart-deckgl/src/DeckGLContainer.tsx
@@ -1,7 +1,3 @@
-/* eslint-disable react/jsx-sort-default-props */
-/* eslint-disable react/sort-prop-types */
-/* eslint-disable react/jsx-handler-names */
-/* eslint-disable react/forbid-prop-types */
 /**
  * Licensed to the Apache Software Foundation (ASF) under one
  * or more contributor license agreements.  See the NOTICE file
@@ -30,32 +26,32 @@ import {
   useImperativeHandle,
   useState,
   isValidElement,
-  useRef,
 } from 'react';
 import { isEqual } from 'lodash';
-import { StaticMap } from 'react-map-gl';
-import DeckGL from '@deck.gl/react';
+import { Map as MapLibreMap } from 'react-map-gl/maplibre';
+import { Map as MapboxMap } from 'react-map-gl/mapbox';
+import mapboxgl from 'mapbox-gl';
 import type { Layer } from '@deck.gl/core';
 import { JsonObject, JsonValue, usePrevious } from '@superset-ui/core';
-import { styled } from '@apache-superset/core/theme';
-import { Device } from '@luma.gl/core';
+import { styled, useTheme } from '@apache-superset/core/theme';
+import { t } from '@apache-superset/core/translation';
+import DeckGLOverlayMapLibre from './components/DeckGLOverlayMapLibre';
+import DeckGLOverlayMapbox from './components/DeckGLOverlayMapbox';
 import Tooltip, { TooltipProps } from './components/Tooltip';
-import 'mapbox-gl/dist/mapbox-gl.css';
+import 'maplibre-gl/dist/maplibre-gl.css';
 import { Viewport } from './utils/fitViewport';
-import {
-  MAPBOX_LAYER_PREFIX,
-  OSM_LAYER_KEYWORDS,
-  TILE_LAYER_PREFIX,
-  buildTileLayer,
-} from './utils';
 
 const TICK = 250; // milliseconds
 
+const DEFAULT_MAP_STYLE =
+  'https://basemaps.cartocdn.com/gl/positron-gl-style/style.json';
+
 export type DeckGLContainerProps = {
   viewport: Viewport;
   setControlValue?: (control: string, value: JsonValue) => void;
   mapStyle?: string;
-  mapboxApiAccessToken: string;
+  mapProvider?: 'maplibre' | 'mapbox';
+  mapboxApiKey?: string;
   children?: ReactNode;
   width: number;
   height: number;
@@ -69,14 +65,6 @@ export const DeckGLContainer = memo(
     const [lastUpdate, setLastUpdate] = useState<number | null>(null);
     const [viewState, setViewState] = useState(props.viewport);
     const prevViewport = usePrevious(props.viewport);
-    const glContextRef = useRef<WebGL2RenderingContext | null>(null);
-
-    useEffect(
-      () => () => {
-        glContextRef.current?.getExtension('WEBGL_lose_context')?.loseContext();
-      },
-      [],
-    );
 
     useImperativeHandle(ref, () => ({ setTooltip }), []);
 
@@ -93,7 +81,7 @@ export const DeckGLContainer = memo(
 
     useEffect(() => {
       const timer = setInterval(tick, TICK);
-      return clearInterval(timer);
+      return () => clearInterval(timer);
     }, [tick]);
 
     useEffect(() => {
@@ -102,31 +90,12 @@ export const DeckGLContainer = memo(
       }
     }, [prevViewport, props.viewport]);
 
-    const onViewStateChange = useCallback(
-      ({ viewState }: { viewState: JsonObject }) => {
-        setViewState(viewState as Viewport);
-        setLastUpdate(Date.now());
-      },
-      [],
-    );
+    const onMove = useCallback((evt: { viewState: JsonObject }) => {
+      setViewState(evt.viewState as Viewport);
+      setLastUpdate(Date.now());
+    }, []);
 
     const layers = useCallback(() => {
-      if (
-        (props.mapStyle?.startsWith(TILE_LAYER_PREFIX) ||
-          OSM_LAYER_KEYWORDS.some((tilek: string) =>
-            props.mapStyle?.includes(tilek),
-          )) &&
-        props.layers.some(
-          l => typeof l !== 'function' && l?.id === 'tile-layer',
-        ) === false
-      ) {
-        props.layers.unshift(
-          buildTileLayer(
-            (props.mapStyle ?? '').replace(TILE_LAYER_PREFIX, ''),
-            'tile-layer',
-          ),
-        );
-      }
       // Support for layer factory
       if (props.layers.some(l => typeof l === 'function')) {
         return props.layers.map(l =>
@@ -135,7 +104,7 @@ export const DeckGLContainer = memo(
       }
 
       return props.layers as Layer[];
-    }, [props.layers, props.mapStyle]);
+    }, [props.layers]);
 
     const isCustomTooltip = (content: ReactNode): boolean =>
       isValidElement(content) &&
@@ -151,7 +120,35 @@ export const DeckGLContainer = memo(
       return <Tooltip tooltip={tooltipState} />;
     };
 
+    const theme = useTheme();
     const { children = null, height, width } = props;
+    const isMapbox = props.mapProvider === 'mapbox';
+    const mapStyle = props.mapStyle || DEFAULT_MAP_STYLE;
+
+    if (isMapbox && !props.mapboxApiKey) {
+      return (
+        <div
+          style={{
+            width,
+            height,
+            display: 'flex',
+            alignItems: 'center',
+            justifyContent: 'center',
+            padding: 16,
+            textAlign: 'center',
+            color: theme.colorTextSecondary,
+          }}
+        >
+          {t(
+            'Mapbox requires a MAPBOX_API_KEY to be configured on the server.',
+          )}
+        </div>
+      );
+    }
+
+    if (isMapbox && props.mapboxApiKey) {
+      mapboxgl.accessToken = props.mapboxApiKey;
+    }
 
     return (
       <>
@@ -162,28 +159,25 @@ export const DeckGLContainer = memo(
             e.stopPropagation();
           }}
         >
-          <DeckGL
-            controller
-            width={width}
-            height={height}
-            layers={layers()}
-            viewState={viewState}
-            onViewStateChange={onViewStateChange}
-            onAfterRender={(context: {
-              device: Device;
-              gl: WebGL2RenderingContext;
-            }) => {
-              glContextRef.current = context.gl;
-            }}
-          >
-            {props.mapStyle?.startsWith(MAPBOX_LAYER_PREFIX) && (
-              <StaticMap
-                preserveDrawingBuffer
-                mapStyle={props.mapStyle || 'light'}
-                mapboxApiAccessToken={props.mapboxApiAccessToken}
-              />
-            )}
-          </DeckGL>
+          {isMapbox ? (
+            <MapboxMap
+              {...viewState}
+              onMove={onMove}
+              mapStyle={mapStyle}
+              style={{ width, height }}
+            >
+              <DeckGLOverlayMapbox layers={layers()} />
+            </MapboxMap>
+          ) : (
+            <MapLibreMap
+              {...viewState}
+              onMove={onMove}
+              mapStyle={mapStyle}
+              style={{ width, height }}
+            >
+              <DeckGLOverlayMapLibre layers={layers()} />
+            </MapLibreMap>
+          )}
           {children}
         </div>
         {renderTooltip(tooltip)}
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/Multi/Multi.test.tsx b/superset-frontend/plugins/preset-chart-deckgl/src/Multi/Multi.test.tsx
similarity index 99%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/Multi/Multi.test.tsx
rename to superset-frontend/plugins/preset-chart-deckgl/src/Multi/Multi.test.tsx
index abce3761ad3..082c789c6a8 100644
--- a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/Multi/Multi.test.tsx
+++ b/superset-frontend/plugins/preset-chart-deckgl/src/Multi/Multi.test.tsx
@@ -58,7 +58,7 @@ const baseMockProps = {
     viz_type: 'deck_multi',
     deck_slices: [1, 2],
     autozoom: false,
-    mapbox_style: 'mapbox://styles/mapbox/light-v9',
+    map_style: 'mapbox://styles/mapbox/light-v9',
   },
   payload: {
     data: {
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/Multi/Multi.tsx b/superset-frontend/plugins/preset-chart-deckgl/src/Multi/Multi.tsx
similarity index 96%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/Multi/Multi.tsx
rename to superset-frontend/plugins/preset-chart-deckgl/src/Multi/Multi.tsx
index 3c52464a247..f88c9a7590f 100644
--- a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/Multi/Multi.tsx
+++ b/superset-frontend/plugins/preset-chart-deckgl/src/Multi/Multi.tsx
@@ -50,6 +50,7 @@ import {
 import { getExploreLongUrl } from '../utils/explore';
 import layerGenerators from '../layers';
 import fitViewport, { Viewport } from '../utils/fitViewport';
+import { getMapboxApiKey } from '../utils/mapbox';
 import { TooltipProps } from '../components/Tooltip';
 
 import { getPoints as getPointsArc } from '../layers/Arc/Arc';
@@ -377,7 +378,7 @@ const DeckMulti = (props: DeckMultiProps) => {
     );
 
     if (deckSlicesChanged || visibilityFilterChanged) {
-      loadLayers(formData, payload, undefined);
+      loadLayers(formData, payload, visibleDeckLayersFromRedux);
     }
   }, [
     loadLayers,
@@ -387,7 +388,7 @@ const DeckMulti = (props: DeckMultiProps) => {
     props,
   ]);
 
-  const { payload, formData, setControlValue, height, width } = props;
+  const { formData, setControlValue, height, width } = props;
 
   const layers = useMemo(
     () =>
@@ -401,10 +402,15 @@ const DeckMulti = (props: DeckMultiProps) => {
     <MultiWrapper height={height} width={width}>
       <DeckGLContainerStyledWrapper
         ref={containerRef}
-        mapboxApiAccessToken={payload.data.mapboxApiKey}
         viewport={viewport}
         layers={layers}
-        mapStyle={formData.mapbox_style}
+        mapStyle={
+          formData.map_renderer === 'mapbox'
+            ? formData.mapbox_style
+            : formData.maplibre_style
+        }
+        mapProvider={formData.map_renderer === 'mapbox' ? 'mapbox' : 'maplibre'}
+        mapboxApiKey={getMapboxApiKey()}
         setControlValue={setControlValue}
         onViewportChange={setViewport}
         height={height}
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/Multi/controlPanel.test.ts b/superset-frontend/plugins/preset-chart-deckgl/src/Multi/controlPanel.test.ts
similarity index 100%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/Multi/controlPanel.test.ts
rename to superset-frontend/plugins/preset-chart-deckgl/src/Multi/controlPanel.test.ts
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/Multi/controlPanel.ts b/superset-frontend/plugins/preset-chart-deckgl/src/Multi/controlPanel.ts
similarity index 94%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/Multi/controlPanel.ts
rename to superset-frontend/plugins/preset-chart-deckgl/src/Multi/controlPanel.ts
index da720d82868..8379345ad16 100644
--- a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/Multi/controlPanel.ts
+++ b/superset-frontend/plugins/preset-chart-deckgl/src/Multi/controlPanel.ts
@@ -18,7 +18,13 @@
  */
 import { t } from '@apache-superset/core/translation';
 import { validateNonEmpty } from '@superset-ui/core';
-import { viewport, mapboxStyle, autozoom } from '../utilities/Shared_DeckGL';
+import {
+  viewport,
+  mapboxStyle,
+  maplibreStyle,
+  mapProvider,
+  autozoom,
+} from '../utilities/Shared_DeckGL';
 
 export default {
   controlPanelSections: [
@@ -26,7 +32,9 @@ export default {
       label: t('Map'),
       expanded: true,
       controlSetRows: [
+        [mapProvider],
         [mapboxStyle],
+        [maplibreStyle],
         [viewport],
         [autozoom],
         [
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/Multi/images/example-dark.png b/superset-frontend/plugins/preset-chart-deckgl/src/Multi/images/example-dark.png
similarity index 100%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/Multi/images/example-dark.png
rename to superset-frontend/plugins/preset-chart-deckgl/src/Multi/images/example-dark.png
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/Multi/images/example.png b/superset-frontend/plugins/preset-chart-deckgl/src/Multi/images/example.png
similarity index 100%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/Multi/images/example.png
rename to superset-frontend/plugins/preset-chart-deckgl/src/Multi/images/example.png
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/Multi/images/thumbnail-dark.png b/superset-frontend/plugins/preset-chart-deckgl/src/Multi/images/thumbnail-dark.png
similarity index 100%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/Multi/images/thumbnail-dark.png
rename to superset-frontend/plugins/preset-chart-deckgl/src/Multi/images/thumbnail-dark.png
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/Multi/images/thumbnail.png b/superset-frontend/plugins/preset-chart-deckgl/src/Multi/images/thumbnail.png
similarity index 100%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/Multi/images/thumbnail.png
rename to superset-frontend/plugins/preset-chart-deckgl/src/Multi/images/thumbnail.png
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/Multi/images/thumbnailLarge.png b/superset-frontend/plugins/preset-chart-deckgl/src/Multi/images/thumbnailLarge.png
similarity index 100%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/Multi/images/thumbnailLarge.png
rename to superset-frontend/plugins/preset-chart-deckgl/src/Multi/images/thumbnailLarge.png
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/Multi/index.ts b/superset-frontend/plugins/preset-chart-deckgl/src/Multi/index.ts
similarity index 100%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/Multi/index.ts
rename to superset-frontend/plugins/preset-chart-deckgl/src/Multi/index.ts
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/TooltipRow.tsx b/superset-frontend/plugins/preset-chart-deckgl/src/TooltipRow.tsx
similarity index 100%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/TooltipRow.tsx
rename to superset-frontend/plugins/preset-chart-deckgl/src/TooltipRow.tsx
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Grid/buildQuery.ts b/superset-frontend/plugins/preset-chart-deckgl/src/components/DeckGLOverlayMapLibre.tsx
similarity index 69%
copy from superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Grid/buildQuery.ts
copy to superset-frontend/plugins/preset-chart-deckgl/src/components/DeckGLOverlayMapLibre.tsx
index fdd73b19649..5e617464d89 100644
--- a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Grid/buildQuery.ts
+++ b/superset-frontend/plugins/preset-chart-deckgl/src/components/DeckGLOverlayMapLibre.tsx
@@ -16,12 +16,12 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-import { SpatialFormData, buildSpatialQuery } from '../spatialUtils';
+import { useControl } from 'react-map-gl/maplibre';
+import { MapboxOverlay } from '@deck.gl/mapbox';
+import type { MapboxOverlayProps } from '@deck.gl/mapbox';
 
-export interface DeckGridFormData extends SpatialFormData {
-  extruded?: boolean;
-}
-
-export default function buildQuery(formData: DeckGridFormData) {
-  return buildSpatialQuery(formData);
+export default function DeckGLOverlayMapLibre(props: MapboxOverlayProps) {
+  const overlay = useControl<MapboxOverlay>(() => new MapboxOverlay(props));
+  overlay.setProps(props);
+  return null;
 }
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Grid/buildQuery.ts b/superset-frontend/plugins/preset-chart-deckgl/src/components/DeckGLOverlayMapbox.tsx
similarity index 69%
copy from superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Grid/buildQuery.ts
copy to superset-frontend/plugins/preset-chart-deckgl/src/components/DeckGLOverlayMapbox.tsx
index fdd73b19649..57ce7d1461b 100644
--- a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Grid/buildQuery.ts
+++ b/superset-frontend/plugins/preset-chart-deckgl/src/components/DeckGLOverlayMapbox.tsx
@@ -16,12 +16,12 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-import { SpatialFormData, buildSpatialQuery } from '../spatialUtils';
+import { useControl } from 'react-map-gl/mapbox';
+import { MapboxOverlay } from '@deck.gl/mapbox';
+import type { MapboxOverlayProps } from '@deck.gl/mapbox';
 
-export interface DeckGridFormData extends SpatialFormData {
-  extruded?: boolean;
-}
-
-export default function buildQuery(formData: DeckGridFormData) {
-  return buildSpatialQuery(formData);
+export default function DeckGLOverlayMapbox(props: MapboxOverlayProps) {
+  const overlay = useControl<MapboxOverlay>(() => new MapboxOverlay(props));
+  overlay.setProps(props);
+  return null;
 }
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/components/Legend.tsx b/superset-frontend/plugins/preset-chart-deckgl/src/components/Legend.tsx
similarity index 94%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/components/Legend.tsx
rename to superset-frontend/plugins/preset-chart-deckgl/src/components/Legend.tsx
index fe21ce2955d..32b995d08d8 100644
--- a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/components/Legend.tsx
+++ b/superset-frontend/plugins/preset-chart-deckgl/src/components/Legend.tsx
@@ -113,8 +113,14 @@ const Legend = ({
         <a
           href="#"
           role="button"
-          onClick={() => toggleCategory(k)}
-          onDoubleClick={() => showSingleCategory(k)}
+          onClick={e => {
+            e.preventDefault();
+            toggleCategory(k);
+          }}
+          onDoubleClick={e => {
+            e.preventDefault();
+            showSingleCategory(k);
+          }}
         >
           <span style={style}>{icon}</span> {formatCategoryLabel(k)}
         </a>
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/components/Tooltip.tsx b/superset-frontend/plugins/preset-chart-deckgl/src/components/Tooltip.tsx
similarity index 95%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/components/Tooltip.tsx
rename to superset-frontend/plugins/preset-chart-deckgl/src/components/Tooltip.tsx
index 11ed4219d7b..1eca2c1a77f 100644
--- a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/components/Tooltip.tsx
+++ b/superset-frontend/plugins/preset-chart-deckgl/src/components/Tooltip.tsx
@@ -42,8 +42,8 @@ const StyledDiv = styled.div<{
     position: absolute;
     top: ${top}px;
     left: ${left}px;
-    zIndex: 9;
-    pointerEvents: none;
+    z-index: 9;
+    pointer-events: none;
     ${
       variant === 'default'
         ? `
@@ -51,8 +51,8 @@ const StyledDiv = styled.div<{
       margin: ${theme.sizeUnit * 2}px;
       background: ${theme.colorBgElevated};
       color: ${theme.colorText};
-      maxWidth: 300px;
-      fontSize: ${theme.fontSizeSM}px;
+      max-width: 300px;
+      font-size: ${theme.fontSizeSM}px;
       border: 1px solid ${theme.colorBorder};
       border-radius: ${theme.borderRadius}px;
       box-shadow: ${theme.boxShadowSecondary};
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/factory.tsx b/superset-frontend/plugins/preset-chart-deckgl/src/factory.tsx
similarity index 94%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/factory.tsx
rename to superset-frontend/plugins/preset-chart-deckgl/src/factory.tsx
index bc48a52a377..dbe39936391 100644
--- a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/factory.tsx
+++ b/superset-frontend/plugins/preset-chart-deckgl/src/factory.tsx
@@ -19,6 +19,7 @@
 import { memo, useCallback, useEffect, useRef, useState } from 'react';
 import { isEqual } from 'lodash';
 import type { Layer } from '@deck.gl/core';
+import { getMapboxApiKey } from './utils/mapbox';
 import {
   Datasource,
   QueryFormData,
@@ -182,16 +183,23 @@ export function createDeckGLComponent(
       }
     }, [computeLayers, prevFormData, prevFilterState, prevPayload, props]);
 
-    const { formData, payload, setControlValue, height, width } = props;
+    const { formData, setControlValue, height, width } = props;
 
     return (
       <div style={{ position: 'relative' }}>
         <DeckGLContainerStyledWrapper
           ref={containerRef}
-          mapboxApiAccessToken={payload.data.mapboxApiKey}
           viewport={viewport}
           layers={layers}
-          mapStyle={formData.mapbox_style}
+          mapStyle={
+            formData.map_renderer === 'mapbox'
+              ? formData.mapbox_style
+              : formData.maplibre_style
+          }
+          mapProvider={
+            formData.map_renderer === 'mapbox' ? 'mapbox' : 'maplibre'
+          }
+          mapboxApiKey={getMapboxApiKey()}
           setControlValue={setControlValue}
           width={width}
           height={height}
@@ -232,7 +240,6 @@ export function createCategoricalDeckGLComponent(
       <CategoricalDeckGLContainer
         datasource={datasource}
         formData={formData}
-        mapboxApiKey={payload.data.mapboxApiKey}
         setControlValue={setControlValue}
         viewport={viewport}
         getLayer={getLayer}
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/index.ts b/superset-frontend/plugins/preset-chart-deckgl/src/index.ts
similarity index 100%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/index.ts
rename to superset-frontend/plugins/preset-chart-deckgl/src/index.ts
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Arc/Arc.tsx b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Arc/Arc.tsx
similarity index 100%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Arc/Arc.tsx
rename to superset-frontend/plugins/preset-chart-deckgl/src/layers/Arc/Arc.tsx
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Arc/buildQuery.ts b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Arc/buildQuery.ts
similarity index 100%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Arc/buildQuery.ts
rename to superset-frontend/plugins/preset-chart-deckgl/src/layers/Arc/buildQuery.ts
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Arc/controlPanel.ts b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Arc/controlPanel.ts
similarity index 96%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Arc/controlPanel.ts
rename to superset-frontend/plugins/preset-chart-deckgl/src/layers/Arc/controlPanel.ts
index 46c5e44bde3..fdfb38eae90 100644
--- a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Arc/controlPanel.ts
+++ b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Arc/controlPanel.ts
@@ -39,6 +39,8 @@ import {
   legendPosition,
   viewport,
   mapboxStyle,
+  maplibreStyle,
+  mapProvider,
   tooltipContents,
   tooltipTemplate,
   deckGLCategoricalColor,
@@ -86,7 +88,12 @@ const config: ControlPanelConfig = {
     },
     {
       label: t('Map'),
-      controlSetRows: [[mapboxStyle], [autozoom, viewport]],
+      controlSetRows: [
+        [mapProvider],
+        [mapboxStyle],
+        [maplibreStyle],
+        [autozoom, viewport],
+      ],
     },
     {
       label: t('Arc'),
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Arc/images/example-dark.png b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Arc/images/example-dark.png
similarity index 100%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Arc/images/example-dark.png
rename to superset-frontend/plugins/preset-chart-deckgl/src/layers/Arc/images/example-dark.png
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Arc/images/example.png b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Arc/images/example.png
similarity index 100%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Arc/images/example.png
rename to superset-frontend/plugins/preset-chart-deckgl/src/layers/Arc/images/example.png
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Arc/images/thumbnail-dark.png b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Arc/images/thumbnail-dark.png
similarity index 100%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Arc/images/thumbnail-dark.png
rename to superset-frontend/plugins/preset-chart-deckgl/src/layers/Arc/images/thumbnail-dark.png
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Arc/images/thumbnail.png b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Arc/images/thumbnail.png
similarity index 100%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Arc/images/thumbnail.png
rename to superset-frontend/plugins/preset-chart-deckgl/src/layers/Arc/images/thumbnail.png
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Arc/images/thumbnailLarge.png b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Arc/images/thumbnailLarge.png
similarity index 100%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Arc/images/thumbnailLarge.png
rename to superset-frontend/plugins/preset-chart-deckgl/src/layers/Arc/images/thumbnailLarge.png
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Arc/index.ts b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Arc/index.ts
similarity index 100%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Arc/index.ts
rename to superset-frontend/plugins/preset-chart-deckgl/src/layers/Arc/index.ts
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Arc/stories/Arc.stories.tsx b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Arc/stories/Arc.stories.tsx
similarity index 93%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Arc/stories/Arc.stories.tsx
rename to superset-frontend/plugins/preset-chart-deckgl/src/layers/Arc/stories/Arc.stories.tsx
index 645fc9a0fd4..c57e8c21419 100644
--- a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Arc/stories/Arc.stories.tsx
+++ b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Arc/stories/Arc.stories.tsx
@@ -20,7 +20,7 @@
 /* eslint-disable sort-keys */
 /* eslint-disable no-magic-numbers */
 import { SuperChart } from '@superset-ui/core';
-import { ArcChartPlugin } from '@superset-ui/legacy-preset-chart-deckgl';
+import { ArcChartPlugin } from '@superset-ui/preset-chart-deckgl';
 import { withResizableChartDemo } from '@storybook-shared';
 import payload from './payload';
 import { dummyDatasource } from '@storybook-shared';
@@ -28,7 +28,7 @@ import { dummyDatasource } from '@storybook-shared';
 new ArcChartPlugin().configure({ key: 'deck_arc' }).register();
 
 export default {
-  title: 'Legacy Chart Plugins/legacy-preset-chart-deckgl/ArcChartPlugin',
+  title: 'Chart Plugins/preset-chart-deckgl/ArcChartPlugin',
   decorators: [withResizableChartDemo],
   args: {
     strokeWidth: 1,
@@ -90,7 +90,8 @@ export const ArcChartViz = ({
       row_limit: 5000,
       filter_nulls: true,
       adhoc_filters: [],
-      mapbox_style: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png',
+      map_style:
+        'https://basemaps.cartocdn.com/gl/positron-gl-style/style.json',
       viewport: {
         altitude: 1.5,
         bearing: 8.546256357301871,
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Arc/stories/payload.ts b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Arc/stories/payload.ts
similarity index 100%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Arc/stories/payload.ts
rename to superset-frontend/plugins/preset-chart-deckgl/src/layers/Arc/stories/payload.ts
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Arc/transformProps.ts b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Arc/transformProps.ts
similarity index 100%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Arc/transformProps.ts
rename to superset-frontend/plugins/preset-chart-deckgl/src/layers/Arc/transformProps.ts
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Contour/Contour.tsx b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Contour/Contour.tsx
similarity index 100%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Contour/Contour.tsx
rename to superset-frontend/plugins/preset-chart-deckgl/src/layers/Contour/Contour.tsx
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Contour/buildQuery.ts b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Contour/buildQuery.ts
similarity index 100%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Contour/buildQuery.ts
rename to superset-frontend/plugins/preset-chart-deckgl/src/layers/Contour/buildQuery.ts
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Contour/controlPanel.ts b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Contour/controlPanel.ts
similarity index 97%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Contour/controlPanel.ts
rename to superset-frontend/plugins/preset-chart-deckgl/src/layers/Contour/controlPanel.ts
index ee53209ec30..dc394a32e05 100644
--- a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Contour/controlPanel.ts
+++ b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Contour/controlPanel.ts
@@ -30,6 +30,8 @@ import {
   jsOnclickHref,
   jsTooltip,
   mapboxStyle,
+  maplibreStyle,
+  mapProvider,
   spatial,
   viewport,
   tooltipContents,
@@ -55,7 +57,9 @@ const config: ControlPanelConfig = {
       label: t('Map'),
       expanded: true,
       controlSetRows: [
+        [mapProvider],
         [mapboxStyle],
+        [maplibreStyle],
         [autozoom, viewport],
         [
           {
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Contour/getSafeCellSize.test.ts b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Contour/getSafeCellSize.test.ts
similarity index 100%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Contour/getSafeCellSize.test.ts
rename to superset-frontend/plugins/preset-chart-deckgl/src/layers/Contour/getSafeCellSize.test.ts
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Contour/getSafeCellSize.ts b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Contour/getSafeCellSize.ts
similarity index 100%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Contour/getSafeCellSize.ts
rename to superset-frontend/plugins/preset-chart-deckgl/src/layers/Contour/getSafeCellSize.ts
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Contour/images/example-dark.png b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Contour/images/example-dark.png
similarity index 100%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Contour/images/example-dark.png
rename to superset-frontend/plugins/preset-chart-deckgl/src/layers/Contour/images/example-dark.png
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Contour/images/example.png b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Contour/images/example.png
similarity index 100%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Contour/images/example.png
rename to superset-frontend/plugins/preset-chart-deckgl/src/layers/Contour/images/example.png
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Contour/images/thumbnail-dark.png b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Contour/images/thumbnail-dark.png
similarity index 100%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Contour/images/thumbnail-dark.png
rename to superset-frontend/plugins/preset-chart-deckgl/src/layers/Contour/images/thumbnail-dark.png
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Contour/images/thumbnail.png b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Contour/images/thumbnail.png
similarity index 100%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Contour/images/thumbnail.png
rename to superset-frontend/plugins/preset-chart-deckgl/src/layers/Contour/images/thumbnail.png
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Contour/images/thumbnailLarge.png b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Contour/images/thumbnailLarge.png
similarity index 100%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Contour/images/thumbnailLarge.png
rename to superset-frontend/plugins/preset-chart-deckgl/src/layers/Contour/images/thumbnailLarge.png
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Contour/index.ts b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Contour/index.ts
similarity index 100%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Contour/index.ts
rename to superset-frontend/plugins/preset-chart-deckgl/src/layers/Contour/index.ts
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Contour/transformProps.ts b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Contour/transformProps.ts
similarity index 100%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Contour/transformProps.ts
rename to superset-frontend/plugins/preset-chart-deckgl/src/layers/Contour/transformProps.ts
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Geojson/Geojson.test.ts b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Geojson/Geojson.test.ts
similarity index 97%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Geojson/Geojson.test.ts
rename to superset-frontend/plugins/preset-chart-deckgl/src/layers/Geojson/Geojson.test.ts
index bf3b8527df8..6f189dc8c58 100644
--- a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Geojson/Geojson.test.ts
+++ b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Geojson/Geojson.test.ts
@@ -24,9 +24,10 @@ import {
   computeGeoJsonIconOptionsFromFormData,
 } from './Geojson';
 
-jest.mock('@deck.gl/react', () => ({
+jest.mock('react-map-gl/maplibre', () => ({
   __esModule: true,
-  default: () => null,
+  Map: () => null,
+  useControl: () => null,
 }));
 
 test('computeGeoJsonTextOptionsFromJsOutput returns an empty object for non-object input', () => {
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Geojson/Geojson.tsx b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Geojson/Geojson.tsx
similarity index 97%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Geojson/Geojson.tsx
rename to superset-frontend/plugins/preset-chart-deckgl/src/layers/Geojson/Geojson.tsx
index 4ccaaaa8dd7..bebe118c5f8 100644
--- a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Geojson/Geojson.tsx
+++ b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Geojson/Geojson.tsx
@@ -123,6 +123,8 @@ function setTooltipContent(o: JsonObject) {
 }
 
 const getFillColor = (feature: JsonObject, filterStateValue: unknown[]) => {
+  const baseColor = feature?.properties?.fillColor;
+
   if (filterStateValue) {
     if (
       JSON.stringify(feature.geometry.coordinates) ===
@@ -131,11 +133,14 @@ const getFillColor = (feature: JsonObject, filterStateValue: unknown[]) => {
       return HIGHLIGHT_COLOR_ARRAY;
     }
 
-    const fillColor = feature?.properties?.fillColor;
-    fillColor[3] = 125;
-    return fillColor;
+    if (Array.isArray(baseColor) && baseColor.length >= 4) {
+      return [baseColor[0], baseColor[1], baseColor[2], 125];
+    }
+
+    return baseColor ?? HIGHLIGHT_COLOR_ARRAY;
   }
-  return feature?.properties?.fillColor;
+
+  return baseColor;
 };
 const getLineColor = (feature: JsonObject) => feature?.properties?.strokeColor;
 
@@ -410,10 +415,9 @@ const DeckGLGeoJson = (props: DeckGLGeoJsonProps) => {
   return (
     <DeckGLContainerStyledWrapper
       ref={containerRef}
-      mapboxApiAccessToken={payload.data.mapboxApiKey}
       viewport={viewport}
       layers={[layer]}
-      mapStyle={formData.mapbox_style}
+      mapStyle={formData.map_style}
       setControlValue={setControlValue}
       height={height}
       width={width}
diff --git a/superset-frontend/plugins/preset-chart-deckgl/src/layers/Geojson/buildQuery.ts b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Geojson/buildQuery.ts
new file mode 100644
index 00000000000..67b5ddd2327
--- /dev/null
+++ b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Geojson/buildQuery.ts
@@ -0,0 +1,86 @@
+/**
+ * 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 {
+  buildQueryContext,
+  ensureIsArray,
+  QueryFormColumn,
+  QueryObject,
+  QueryObjectFilterClause,
+  SqlaFormData,
+} from '@superset-ui/core';
+import {
+  addJsColumnsToColumns,
+  addTooltipColumnsToQuery,
+} from '../buildQueryUtils';
+
+export interface DeckGeoJsonFormData extends SqlaFormData {
+  geojson?: string;
+  filter_nulls?: boolean;
+  js_columns?: string[];
+  tooltip_contents?: unknown[];
+}
+
+export default function buildQuery(formData: DeckGeoJsonFormData) {
+  const {
+    geojson,
+    filter_nulls = true,
+    js_columns,
+    tooltip_contents,
+  } = formData;
+
+  if (!geojson) {
+    throw new Error('GeoJSON column is required for GeoJSON charts');
+  }
+
+  return buildQueryContext(formData, (baseQueryObject: QueryObject) => {
+    let columns: QueryFormColumn[] = [
+      ...ensureIsArray(baseQueryObject.columns || []),
+      geojson,
+    ];
+
+    // Add js_columns
+    const columnStrings = columns.map(col =>
+      typeof col === 'string' ? col : col.label || col.sqlExpression || '',
+    );
+    const withJsColumns = addJsColumnsToColumns(columnStrings, js_columns);
+    columns = withJsColumns as QueryFormColumn[];
+
+    // Add tooltip columns
+    columns = addTooltipColumnsToQuery(columns, tooltip_contents);
+
+    // Add null filter for geojson column
+    const filters: QueryObjectFilterClause[] = ensureIsArray(
+      baseQueryObject.filters || [],
+    );
+    if (filter_nulls) {
+      filters.push({ col: geojson, op: 'IS NOT NULL' });
+    }
+
+    return [
+      {
+        ...baseQueryObject,
+        columns,
+        metrics: [],
+        groupby: [],
+        filters,
+        is_timeseries: false,
+      },
+    ];
+  });
+}
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Geojson/controlPanel.ts b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Geojson/controlPanel.ts
similarity index 98%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Geojson/controlPanel.ts
rename to superset-frontend/plugins/preset-chart-deckgl/src/layers/Geojson/controlPanel.ts
index cfb8c89cba6..6326968bad9 100644
--- a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Geojson/controlPanel.ts
+++ b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Geojson/controlPanel.ts
@@ -37,6 +37,8 @@ import {
   extruded,
   viewport,
   mapboxStyle,
+  maplibreStyle,
+  mapProvider,
   autozoom,
   lineWidth,
   tooltipContents,
@@ -79,7 +81,12 @@ const config: ControlPanelConfig = {
     },
     {
       label: t('Map'),
-      controlSetRows: [[mapboxStyle, viewport], [autozoom]],
+      controlSetRows: [
+        [mapProvider],
+        [mapboxStyle],
+        [maplibreStyle],
+        [viewport, autozoom],
+      ],
     },
     {
       label: t('GeoJson Settings'),
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Geojson/images/example-dark.png b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Geojson/images/example-dark.png
similarity index 100%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Geojson/images/example-dark.png
rename to superset-frontend/plugins/preset-chart-deckgl/src/layers/Geojson/images/example-dark.png
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Geojson/images/example.png b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Geojson/images/example.png
similarity index 100%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Geojson/images/example.png
rename to superset-frontend/plugins/preset-chart-deckgl/src/layers/Geojson/images/example.png
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Geojson/images/thumbnail-dark.png b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Geojson/images/thumbnail-dark.png
similarity index 100%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Geojson/images/thumbnail-dark.png
rename to superset-frontend/plugins/preset-chart-deckgl/src/layers/Geojson/images/thumbnail-dark.png
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Geojson/images/thumbnail.png b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Geojson/images/thumbnail.png
similarity index 100%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Geojson/images/thumbnail.png
rename to superset-frontend/plugins/preset-chart-deckgl/src/layers/Geojson/images/thumbnail.png
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Geojson/images/thumbnailLarge.png b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Geojson/images/thumbnailLarge.png
similarity index 100%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Geojson/images/thumbnailLarge.png
rename to superset-frontend/plugins/preset-chart-deckgl/src/layers/Geojson/images/thumbnailLarge.png
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Geojson/index.ts b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Geojson/index.ts
similarity index 94%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Geojson/index.ts
rename to superset-frontend/plugins/preset-chart-deckgl/src/layers/Geojson/index.ts
index 9b2a8733b1e..618beff2b46 100644
--- a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Geojson/index.ts
+++ b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Geojson/index.ts
@@ -22,7 +22,6 @@ import thumbnail from './images/thumbnail.png';
 import thumbnailDark from './images/thumbnail-dark.png';
 import example from './images/example.png';
 import exampleDark from './images/example-dark.png';
-import transformProps from '../../transformProps';
 import controlPanel from './controlPanel';
 
 const metadata = new ChartMetadata({
@@ -35,7 +34,6 @@ const metadata = new ChartMetadata({
   name: t('deck.gl Geojson'),
   thumbnail,
   thumbnailDark,
-  useLegacyApi: true,
   tags: [t('deckGL'), t('2D')],
   behaviors: [Behavior.InteractiveChart],
 });
@@ -44,9 +42,10 @@ export default class GeojsonChartPlugin extends ChartPlugin {
   constructor() {
     super({
       loadChart: () => import('./Geojson'),
+      loadTransformProps: () => import('./transformProps'),
+      loadBuildQuery: () => import('./buildQuery'),
       controlPanel,
       metadata,
-      transformProps,
     });
   }
 }
diff --git a/superset-frontend/plugins/preset-chart-deckgl/src/layers/Geojson/transformProps.ts b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Geojson/transformProps.ts
new file mode 100644
index 00000000000..572df299ade
--- /dev/null
+++ b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Geojson/transformProps.ts
@@ -0,0 +1,50 @@
+/**
+ * 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, getColumnLabel } from '@superset-ui/core';
+import { getRecordsFromQuery } from '../transformUtils';
+import { DataRecord } from '../spatialUtils';
+import { createBaseTransformResult } from '../transformUtils';
+
+export default function transformProps(chartProps: ChartProps) {
+  const { rawFormData: formData } = chartProps;
+  const geojsonCol = formData.geojson
+    ? getColumnLabel(formData.geojson)
+    : undefined;
+
+  if (!geojsonCol) {
+    return createBaseTransformResult(chartProps, []);
+  }
+
+  const records = getRecordsFromQuery(chartProps.queriesData);
+
+  // Parse each record's geojson column value (replicates backend DeckGeoJson.get_properties)
+  const features = records
+    .map((record: DataRecord) => {
+      const geojsonStr = record[geojsonCol];
+      if (geojsonStr == null) return null;
+      try {
+        return JSON.parse(String(geojsonStr));
+      } catch {
+        return null;
+      }
+    })
+    .filter(Boolean);
+
+  return createBaseTransformResult(chartProps, features);
+}
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Grid/Grid.tsx b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Grid/Grid.tsx
similarity index 100%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Grid/Grid.tsx
rename to superset-frontend/plugins/preset-chart-deckgl/src/layers/Grid/Grid.tsx
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Grid/buildQuery.ts b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Grid/buildQuery.ts
similarity index 100%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Grid/buildQuery.ts
rename to superset-frontend/plugins/preset-chart-deckgl/src/layers/Grid/buildQuery.ts
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Grid/controlPanel.ts b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Grid/controlPanel.ts
similarity index 96%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Grid/controlPanel.ts
rename to superset-frontend/plugins/preset-chart-deckgl/src/layers/Grid/controlPanel.ts
index c0a280e4b75..a6363ff5ef2 100644
--- a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Grid/controlPanel.ts
+++ b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Grid/controlPanel.ts
@@ -34,6 +34,8 @@ import {
   viewport,
   spatial,
   mapboxStyle,
+  maplibreStyle,
+  mapProvider,
   tooltipContents,
   tooltipTemplate,
   legendPosition,
@@ -59,7 +61,9 @@ const config: ControlPanelConfig = {
     {
       label: t('Map'),
       controlSetRows: [
+        [mapProvider],
         [mapboxStyle],
+        [maplibreStyle],
         [viewport],
         ...generateDeckGLColorSchemeControls({
           defaultSchemeType: COLOR_SCHEME_TYPES.categorical_palette,
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Grid/images/example-dark.png b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Grid/images/example-dark.png
similarity index 100%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Grid/images/example-dark.png
rename to superset-frontend/plugins/preset-chart-deckgl/src/layers/Grid/images/example-dark.png
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Grid/images/example.png b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Grid/images/example.png
similarity index 100%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Grid/images/example.png
rename to superset-frontend/plugins/preset-chart-deckgl/src/layers/Grid/images/example.png
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Grid/images/thumbnail-dark.png b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Grid/images/thumbnail-dark.png
similarity index 100%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Grid/images/thumbnail-dark.png
rename to superset-frontend/plugins/preset-chart-deckgl/src/layers/Grid/images/thumbnail-dark.png
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Grid/images/thumbnail.png b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Grid/images/thumbnail.png
similarity index 100%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Grid/images/thumbnail.png
rename to superset-frontend/plugins/preset-chart-deckgl/src/layers/Grid/images/thumbnail.png
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Grid/images/thumbnailLarge.png b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Grid/images/thumbnailLarge.png
similarity index 100%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Grid/images/thumbnailLarge.png
rename to superset-frontend/plugins/preset-chart-deckgl/src/layers/Grid/images/thumbnailLarge.png
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Grid/index.ts b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Grid/index.ts
similarity index 100%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Grid/index.ts
rename to superset-frontend/plugins/preset-chart-deckgl/src/layers/Grid/index.ts
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Grid/stories/Grid.stories.tsx b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Grid/stories/Grid.stories.tsx
similarity index 92%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Grid/stories/Grid.stories.tsx
rename to superset-frontend/plugins/preset-chart-deckgl/src/layers/Grid/stories/Grid.stories.tsx
index 52e08bdd29e..6ef79fc2213 100644
--- a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Grid/stories/Grid.stories.tsx
+++ b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Grid/stories/Grid.stories.tsx
@@ -20,14 +20,14 @@
 /* eslint-disable sort-keys */
 /* eslint-disable no-magic-numbers */
 import { SuperChart } from '@superset-ui/core';
-import { GridChartPlugin } from '@superset-ui/legacy-preset-chart-deckgl';
+import { GridChartPlugin } from '@superset-ui/preset-chart-deckgl';
 import { withResizableChartDemo, dummyDatasource } from '@storybook-shared';
 import payload from './payload';
 
 new GridChartPlugin().configure({ key: 'deck_grid' }).register();
 
 export default {
-  title: 'Legacy Chart Plugins/legacy-preset-chart-deckgl/GridChartPlugin',
+  title: 'Chart Plugins/preset-chart-deckgl/GridChartPlugin',
   decorators: [withResizableChartDemo],
   args: {
     gridSize: 120,
@@ -79,7 +79,8 @@ export const GridChartViz = ({
       row_limit: 5000,
       filter_nulls: true,
       adhoc_filters: [],
-      mapbox_style: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png',
+      map_style:
+        'https://basemaps.cartocdn.com/gl/positron-gl-style/style.json',
       viewport: {
         bearing: 155.80099696026355,
         latitude: 37.7942314882596,
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Grid/stories/payload.ts b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Grid/stories/payload.ts
similarity index 100%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Grid/stories/payload.ts
rename to superset-frontend/plugins/preset-chart-deckgl/src/layers/Grid/stories/payload.ts
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Grid/transformProps.ts b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Grid/transformProps.ts
similarity index 100%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Grid/transformProps.ts
rename to superset-frontend/plugins/preset-chart-deckgl/src/layers/Grid/transformProps.ts
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Heatmap/Heatmap.tsx b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Heatmap/Heatmap.tsx
similarity index 95%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Heatmap/Heatmap.tsx
rename to superset-frontend/plugins/preset-chart-deckgl/src/layers/Heatmap/Heatmap.tsx
index 4fffc554080..963ddcdbd62 100644
--- a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Heatmap/Heatmap.tsx
+++ b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Heatmap/Heatmap.tsx
@@ -50,11 +50,11 @@ function setTooltipContent(formData: QueryFormData) {
           label={`${t('Longitude and Latitude')}: `}
           value={`${lon?.toFixed(6)}, ${lat?.toFixed(6)}`}
         />
-        <TooltipRow label="LON: " value={lon?.toFixed(6)} />
-        <TooltipRow label="LAT: " value={lat?.toFixed(6)} />
+        <TooltipRow label={`${t('LON')}: `} value={lon?.toFixed(6)} />
+        <TooltipRow label={`${t('LAT')}: `} value={lat?.toFixed(6)} />
         <TooltipRow
           label={`${metricLabel}: `}
-          value={`${o.object?.weight || o.object?.value || 'Aggregated Cell'}`}
+          value={`${o.object?.weight ?? o.object?.value ?? 'Aggregated Cell'}`}
         />
         {hasCustomTooltip && !hasObjectData && (
           <TooltipRow
@@ -188,7 +188,7 @@ export const getHighlightLayer: GetLayerType<HeatmapLayer> = ({
   );
 
   return new HeatmapLayer({
-    id: `heatmap-layer-${fd.slice_id}` as const,
+    id: `heatmap-highlight-layer-${fd.slice_id}` as const,
     data: dataInside,
     intensity,
     radiusPixels,
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Heatmap/buildQuery.ts b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Heatmap/buildQuery.ts
similarity index 100%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Heatmap/buildQuery.ts
rename to superset-frontend/plugins/preset-chart-deckgl/src/layers/Heatmap/buildQuery.ts
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Heatmap/controlPanel.ts b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Heatmap/controlPanel.ts
similarity index 98%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Heatmap/controlPanel.ts
rename to superset-frontend/plugins/preset-chart-deckgl/src/layers/Heatmap/controlPanel.ts
index efeda7d0b15..9501ade372f 100644
--- a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Heatmap/controlPanel.ts
+++ b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Heatmap/controlPanel.ts
@@ -37,6 +37,8 @@ import {
   jsOnclickHref,
   jsTooltip,
   mapboxStyle,
+  maplibreStyle,
+  mapProvider,
   spatial,
   viewport,
   tooltipContents,
@@ -105,7 +107,9 @@ const config: ControlPanelConfig = {
     {
       label: t('Map'),
       controlSetRows: [
+        [mapProvider],
         [mapboxStyle],
+        [maplibreStyle],
         [viewport],
         [
           {
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Heatmap/images/example-dark.png b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Heatmap/images/example-dark.png
similarity index 100%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Heatmap/images/example-dark.png
rename to superset-frontend/plugins/preset-chart-deckgl/src/layers/Heatmap/images/example-dark.png
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Heatmap/images/example.png b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Heatmap/images/example.png
similarity index 100%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Heatmap/images/example.png
rename to superset-frontend/plugins/preset-chart-deckgl/src/layers/Heatmap/images/example.png
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Heatmap/images/thumbnail-dark.png b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Heatmap/images/thumbnail-dark.png
similarity index 100%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Heatmap/images/thumbnail-dark.png
rename to superset-frontend/plugins/preset-chart-deckgl/src/layers/Heatmap/images/thumbnail-dark.png
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Heatmap/images/thumbnail.png b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Heatmap/images/thumbnail.png
similarity index 100%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Heatmap/images/thumbnail.png
rename to superset-frontend/plugins/preset-chart-deckgl/src/layers/Heatmap/images/thumbnail.png
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Heatmap/images/thumbnailLarge.png b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Heatmap/images/thumbnailLarge.png
similarity index 100%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Heatmap/images/thumbnailLarge.png
rename to superset-frontend/plugins/preset-chart-deckgl/src/layers/Heatmap/images/thumbnailLarge.png
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Heatmap/index.ts b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Heatmap/index.ts
similarity index 100%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Heatmap/index.ts
rename to superset-frontend/plugins/preset-chart-deckgl/src/layers/Heatmap/index.ts
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Heatmap/transformProps.ts b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Heatmap/transformProps.ts
similarity index 100%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Heatmap/transformProps.ts
rename to superset-frontend/plugins/preset-chart-deckgl/src/layers/Heatmap/transformProps.ts
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Hex/Hex.tsx b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Hex/Hex.tsx
similarity index 100%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Hex/Hex.tsx
rename to superset-frontend/plugins/preset-chart-deckgl/src/layers/Hex/Hex.tsx
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Hex/buildQuery.ts b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Hex/buildQuery.ts
similarity index 100%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Hex/buildQuery.ts
rename to superset-frontend/plugins/preset-chart-deckgl/src/layers/Hex/buildQuery.ts
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Hex/controlPanel.ts b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Hex/controlPanel.ts
similarity index 97%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Hex/controlPanel.ts
rename to superset-frontend/plugins/preset-chart-deckgl/src/layers/Hex/controlPanel.ts
index df0cf267ed8..d1f53eae519 100644
--- a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Hex/controlPanel.ts
+++ b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Hex/controlPanel.ts
@@ -32,6 +32,8 @@ import {
   jsOnclickHref,
   jsTooltip,
   mapboxStyle,
+  maplibreStyle,
+  mapProvider,
   spatial,
   viewport,
   tooltipContents,
@@ -57,7 +59,9 @@ const config: ControlPanelConfig = {
     {
       label: t('Map'),
       controlSetRows: [
+        [mapProvider],
         [mapboxStyle],
+        [maplibreStyle],
         ...generateDeckGLColorSchemeControls({
           defaultSchemeType: COLOR_SCHEME_TYPES.categorical_palette,
           disableCategoricalColumn: true,
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Hex/images/example-dark.png b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Hex/images/example-dark.png
similarity index 100%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Hex/images/example-dark.png
rename to superset-frontend/plugins/preset-chart-deckgl/src/layers/Hex/images/example-dark.png
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Hex/images/example.png b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Hex/images/example.png
similarity index 100%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Hex/images/example.png
rename to superset-frontend/plugins/preset-chart-deckgl/src/layers/Hex/images/example.png
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Hex/images/thumbnail-dark.png b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Hex/images/thumbnail-dark.png
similarity index 100%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Hex/images/thumbnail-dark.png
rename to superset-frontend/plugins/preset-chart-deckgl/src/layers/Hex/images/thumbnail-dark.png
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Hex/images/thumbnail.png b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Hex/images/thumbnail.png
similarity index 100%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Hex/images/thumbnail.png
rename to superset-frontend/plugins/preset-chart-deckgl/src/layers/Hex/images/thumbnail.png
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Hex/images/thumbnailLarge.png b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Hex/images/thumbnailLarge.png
similarity index 100%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Hex/images/thumbnailLarge.png
rename to superset-frontend/plugins/preset-chart-deckgl/src/layers/Hex/images/thumbnailLarge.png
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Hex/index.ts b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Hex/index.ts
similarity index 100%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Hex/index.ts
rename to superset-frontend/plugins/preset-chart-deckgl/src/layers/Hex/index.ts
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Hex/stories/Hex.stories.tsx b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Hex/stories/Hex.stories.tsx
similarity index 92%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Hex/stories/Hex.stories.tsx
rename to superset-frontend/plugins/preset-chart-deckgl/src/layers/Hex/stories/Hex.stories.tsx
index 9a308eea41d..756f1733a5d 100644
--- a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Hex/stories/Hex.stories.tsx
+++ b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Hex/stories/Hex.stories.tsx
@@ -20,14 +20,14 @@
 /* eslint-disable sort-keys */
 /* eslint-disable no-magic-numbers */
 import { SuperChart } from '@superset-ui/core';
-import { HexChartPlugin } from '@superset-ui/legacy-preset-chart-deckgl';
+import { HexChartPlugin } from '@superset-ui/preset-chart-deckgl';
 import { withResizableChartDemo, dummyDatasource } from '@storybook-shared';
 import payload from './payload';
 
 new HexChartPlugin().configure({ key: 'deck_hex' }).register();
 
 export default {
-  title: 'Legacy Chart Plugins/legacy-preset-chart-deckgl/HexChartPlugin',
+  title: 'Chart Plugins/preset-chart-deckgl/HexChartPlugin',
   decorators: [withResizableChartDemo],
   args: {
     gridSize: 40,
@@ -79,7 +79,8 @@ export const HexChartViz = ({
       row_limit: 5000,
       filter_nulls: true,
       adhoc_filters: [],
-      mapbox_style: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png',
+      map_style:
+        'https://basemaps.cartocdn.com/gl/positron-gl-style/style.json',
       viewport: {
         bearing: -2.3984797349335167,
         latitude: 37.789795085160335,
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Hex/stories/payload.ts b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Hex/stories/payload.ts
similarity index 100%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Hex/stories/payload.ts
rename to superset-frontend/plugins/preset-chart-deckgl/src/layers/Hex/stories/payload.ts
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Hex/transformProps.ts b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Hex/transformProps.ts
similarity index 100%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Hex/transformProps.ts
rename to superset-frontend/plugins/preset-chart-deckgl/src/layers/Hex/transformProps.ts
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Path/Path.test.tsx b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Path/Path.test.tsx
similarity index 100%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Path/Path.test.tsx
rename to superset-frontend/plugins/preset-chart-deckgl/src/layers/Path/Path.test.tsx
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Path/Path.tsx b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Path/Path.tsx
similarity index 99%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Path/Path.tsx
rename to superset-frontend/plugins/preset-chart-deckgl/src/layers/Path/Path.tsx
index 7408676df70..83fa5802750 100644
--- a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Path/Path.tsx
+++ b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Path/Path.tsx
@@ -116,7 +116,7 @@ export const getHighlightLayer: GetLayerType<PathLayer> = function ({
 
   const filteredData = data.filter(
     (d: JsonObject) =>
-      JSON.stringify(d.path).replaceAll(' ', '') === filterState?.value[0],
+      JSON.stringify(d.path).replaceAll(' ', '') === filterState?.value?.[0],
   );
 
   return new PathLayer({
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Path/buildQuery.ts b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Path/buildQuery.ts
similarity index 100%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Path/buildQuery.ts
rename to superset-frontend/plugins/preset-chart-deckgl/src/layers/Path/buildQuery.ts
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Path/controlPanel.ts b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Path/controlPanel.ts
similarity index 97%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Path/controlPanel.ts
rename to superset-frontend/plugins/preset-chart-deckgl/src/layers/Path/controlPanel.ts
index 82248cc4923..14673499680 100644
--- a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Path/controlPanel.ts
+++ b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Path/controlPanel.ts
@@ -30,6 +30,8 @@ import {
   lineType,
   reverseLongLat,
   mapboxStyle,
+  maplibreStyle,
+  mapProvider,
   tooltipContents,
   tooltipTemplate,
 } from '../../utilities/Shared_DeckGL';
@@ -65,7 +67,9 @@ const config: ControlPanelConfig = {
       label: t('Map'),
       expanded: true,
       controlSetRows: [
+        [mapProvider],
         [mapboxStyle],
+        [maplibreStyle],
         [viewport],
         ['color_picker'],
         [lineWidth],
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Path/images/example-dark.png b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Path/images/example-dark.png
similarity index 100%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Path/images/example-dark.png
rename to superset-frontend/plugins/preset-chart-deckgl/src/layers/Path/images/example-dark.png
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Path/images/example.png b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Path/images/example.png
similarity index 100%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Path/images/example.png
rename to superset-frontend/plugins/preset-chart-deckgl/src/layers/Path/images/example.png
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Path/images/thumbnail-dark.png b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Path/images/thumbnail-dark.png
similarity index 100%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Path/images/thumbnail-dark.png
rename to superset-frontend/plugins/preset-chart-deckgl/src/layers/Path/images/thumbnail-dark.png
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Path/images/thumbnail.png b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Path/images/thumbnail.png
similarity index 100%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Path/images/thumbnail.png
rename to superset-frontend/plugins/preset-chart-deckgl/src/layers/Path/images/thumbnail.png
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Path/images/thumbnailLarge.png b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Path/images/thumbnailLarge.png
similarity index 100%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Path/images/thumbnailLarge.png
rename to superset-frontend/plugins/preset-chart-deckgl/src/layers/Path/images/thumbnailLarge.png
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Path/index.ts b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Path/index.ts
similarity index 100%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Path/index.ts
rename to superset-frontend/plugins/preset-chart-deckgl/src/layers/Path/index.ts
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Path/stories/Path.stories.tsx b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Path/stories/Path.stories.tsx
similarity index 92%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Path/stories/Path.stories.tsx
rename to superset-frontend/plugins/preset-chart-deckgl/src/layers/Path/stories/Path.stories.tsx
index 0d5651ad946..0738a17868c 100644
--- a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Path/stories/Path.stories.tsx
+++ b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Path/stories/Path.stories.tsx
@@ -21,14 +21,14 @@
 /* eslint-disable no-magic-numbers */
 import { SuperChart } from '@superset-ui/core';
 import { useTheme } from '@apache-superset/core/theme';
-import { PathChartPlugin } from '@superset-ui/legacy-preset-chart-deckgl';
+import { PathChartPlugin } from '@superset-ui/preset-chart-deckgl';
 import { withResizableChartDemo, dummyDatasource } from '@storybook-shared';
 import payload from './payload';
 
 new PathChartPlugin().configure({ key: 'deck_path' }).register();
 
 export default {
-  title: 'Legacy Chart Plugins/legacy-preset-chart-deckgl/PathChartPlugin',
+  title: 'Chart Plugins/preset-chart-deckgl/PathChartPlugin',
   decorators: [withResizableChartDemo],
   args: {
     lineWidth: 150,
@@ -75,7 +75,8 @@ export const PathChartViz = ({
         row_limit: 5000,
         filter_nulls: true,
         adhoc_filters: [],
-        mapbox_style: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png',
+        map_style:
+          'https://basemaps.cartocdn.com/gl/positron-gl-style/style.json',
         viewport: {
           altitude: 1.5,
           bearing: 0,
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Path/stories/payload.ts b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Path/stories/payload.ts
similarity index 100%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Path/stories/payload.ts
rename to superset-frontend/plugins/preset-chart-deckgl/src/layers/Path/stories/payload.ts
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Path/transformProps.ts b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Path/transformProps.ts
similarity index 100%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Path/transformProps.ts
rename to superset-frontend/plugins/preset-chart-deckgl/src/layers/Path/transformProps.ts
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Polygon/Polygon.test.tsx b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Polygon/Polygon.test.tsx
similarity index 99%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Polygon/Polygon.test.tsx
rename to superset-frontend/plugins/preset-chart-deckgl/src/layers/Polygon/Polygon.test.tsx
index d6e1d640228..d107dc42771 100644
--- a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Polygon/Polygon.test.tsx
+++ b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Polygon/Polygon.test.tsx
@@ -60,7 +60,7 @@ const mockProps = {
     legend_position: 'tr',
     legend_format: '.2f',
     autozoom: false,
-    mapbox_style: 'mapbox://styles/mapbox/light-v9',
+    map_style: 'mapbox://styles/mapbox/light-v9',
     opacity: 80,
     filled: true,
     stroked: true,
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Polygon/Polygon.tsx b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Polygon/Polygon.tsx
similarity index 99%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Polygon/Polygon.tsx
rename to superset-frontend/plugins/preset-chart-deckgl/src/layers/Polygon/Polygon.tsx
index 0a4d8088b28..08ece720b85 100644
--- a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Polygon/Polygon.tsx
+++ b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Polygon/Polygon.tsx
@@ -347,8 +347,7 @@ const DeckGLPolygon = (props: DeckGLPolygonProps) => {
         viewport={viewport}
         layers={getLayers()}
         setControlValue={setControlValue}
-        mapStyle={formData.mapbox_style}
-        mapboxApiAccessToken={payload.data.mapboxApiKey}
+        mapStyle={formData.map_style}
         width={props.width}
         height={props.height}
       />
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Polygon/buildQuery.test.ts b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Polygon/buildQuery.test.ts
similarity index 100%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Polygon/buildQuery.test.ts
rename to superset-frontend/plugins/preset-chart-deckgl/src/layers/Polygon/buildQuery.test.ts
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Polygon/buildQuery.ts b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Polygon/buildQuery.ts
similarity index 100%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Polygon/buildQuery.ts
rename to superset-frontend/plugins/preset-chart-deckgl/src/layers/Polygon/buildQuery.ts
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Polygon/controlPanel.ts b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Polygon/controlPanel.ts
similarity index 97%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Polygon/controlPanel.ts
rename to superset-frontend/plugins/preset-chart-deckgl/src/layers/Polygon/controlPanel.ts
index 45198a9d291..010e079d00f 100644
--- a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Polygon/controlPanel.ts
+++ b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Polygon/controlPanel.ts
@@ -44,6 +44,8 @@ import {
   lineType,
   reverseLongLat,
   mapboxStyle,
+  maplibreStyle,
+  mapProvider,
   deckGLCategoricalColorSchemeTypeSelect,
   deckGLLinearColorSchemeSelect,
   deckGLColorBreakpointsSelect,
@@ -98,7 +100,13 @@ const config: ControlPanelConfig = {
     {
       label: t('Map'),
       expanded: true,
-      controlSetRows: [[mapboxStyle], [viewport], [autozoom]],
+      controlSetRows: [
+        [mapProvider],
+        [mapboxStyle],
+        [maplibreStyle],
+        [viewport],
+        [autozoom],
+      ],
     },
     {
       label: t('Polygon Settings'),
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Polygon/images/example-dark.png b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Polygon/images/example-dark.png
similarity index 100%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Polygon/images/example-dark.png
rename to superset-frontend/plugins/preset-chart-deckgl/src/layers/Polygon/images/example-dark.png
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Polygon/images/example.png b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Polygon/images/example.png
similarity index 100%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Polygon/images/example.png
rename to superset-frontend/plugins/preset-chart-deckgl/src/layers/Polygon/images/example.png
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Polygon/images/thumbnail-dark.png b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Polygon/images/thumbnail-dark.png
similarity index 100%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Polygon/images/thumbnail-dark.png
rename to superset-frontend/plugins/preset-chart-deckgl/src/layers/Polygon/images/thumbnail-dark.png
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Polygon/images/thumbnail.png b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Polygon/images/thumbnail.png
similarity index 100%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Polygon/images/thumbnail.png
rename to superset-frontend/plugins/preset-chart-deckgl/src/layers/Polygon/images/thumbnail.png
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Polygon/images/thumbnailLarge.png b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Polygon/images/thumbnailLarge.png
similarity index 100%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Polygon/images/thumbnailLarge.png
rename to superset-frontend/plugins/preset-chart-deckgl/src/layers/Polygon/images/thumbnailLarge.png
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Polygon/index.ts b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Polygon/index.ts
similarity index 100%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Polygon/index.ts
rename to superset-frontend/plugins/preset-chart-deckgl/src/layers/Polygon/index.ts
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Polygon/stories/Polygon.stories.tsx b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Polygon/stories/Polygon.stories.tsx
similarity index 94%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Polygon/stories/Polygon.stories.tsx
rename to superset-frontend/plugins/preset-chart-deckgl/src/layers/Polygon/stories/Polygon.stories.tsx
index a212c734b93..40964ebd435 100644
--- a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Polygon/stories/Polygon.stories.tsx
+++ b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Polygon/stories/Polygon.stories.tsx
@@ -20,7 +20,7 @@
 /* eslint-disable sort-keys */
 /* eslint-disable no-magic-numbers */
 import { SuperChart } from '@superset-ui/core';
-import { PolygonChartPlugin } from '@superset-ui/legacy-preset-chart-deckgl';
+import { PolygonChartPlugin } from '@superset-ui/preset-chart-deckgl';
 import { withResizableChartDemo, dummyDatasource } from '@storybook-shared';
 import payload from './payload';
 import geojsonPayload from './geojsonPayload';
@@ -28,7 +28,7 @@ import geojsonPayload from './geojsonPayload';
 new PolygonChartPlugin().configure({ key: 'deck_polygon' }).register();
 
 export default {
-  title: 'Legacy Chart Plugins/legacy-preset-chart-deckgl/PolygonChartPlugin',
+  title: 'Chart Plugins/preset-chart-deckgl/PolygonChartPlugin',
   decorators: [withResizableChartDemo],
   args: {
     filled: true,
@@ -104,7 +104,8 @@ export const PolygonChartViz = ({
       row_limit: 10000,
       reverse_long_lat: false,
       filter_nulls: true,
-      mapbox_style: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png',
+      map_style:
+        'https://basemaps.cartocdn.com/gl/positron-gl-style/style.json',
       viewport: {
         altitude: 1.5,
         bearing: 37.89506450385642,
@@ -181,7 +182,8 @@ export const GeojsonPolygonViz = ({
       row_limit: 10000,
       reverse_long_lat: false,
       filter_nulls: true,
-      mapbox_style: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png',
+      map_style:
+        'https://basemaps.cartocdn.com/gl/positron-gl-style/style.json',
       viewport: {
         longitude: 6.85236157047845,
         latitude: 31.222656842808707,
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Polygon/stories/geojsonPayload.ts b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Polygon/stories/geojsonPayload.ts
similarity index 100%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Polygon/stories/geojsonPayload.ts
rename to superset-frontend/plugins/preset-chart-deckgl/src/layers/Polygon/stories/geojsonPayload.ts
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Polygon/stories/payload.ts b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Polygon/stories/payload.ts
similarity index 100%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Polygon/stories/payload.ts
rename to superset-frontend/plugins/preset-chart-deckgl/src/layers/Polygon/stories/payload.ts
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Polygon/transformProps.test.ts b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Polygon/transformProps.test.ts
similarity index 100%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Polygon/transformProps.test.ts
rename to superset-frontend/plugins/preset-chart-deckgl/src/layers/Polygon/transformProps.test.ts
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Polygon/transformProps.ts b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Polygon/transformProps.ts
similarity index 100%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Polygon/transformProps.ts
rename to superset-frontend/plugins/preset-chart-deckgl/src/layers/Polygon/transformProps.ts
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Scatter/Scatter.tsx b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Scatter/Scatter.tsx
similarity index 100%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Scatter/Scatter.tsx
rename to superset-frontend/plugins/preset-chart-deckgl/src/layers/Scatter/Scatter.tsx
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Scatter/buildQuery.test.ts b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Scatter/buildQuery.test.ts
similarity index 100%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Scatter/buildQuery.test.ts
rename to superset-frontend/plugins/preset-chart-deckgl/src/layers/Scatter/buildQuery.test.ts
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Scatter/buildQuery.ts b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Scatter/buildQuery.ts
similarity index 100%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Scatter/buildQuery.ts
rename to superset-frontend/plugins/preset-chart-deckgl/src/layers/Scatter/buildQuery.ts
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Scatter/controlPanel.ts b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Scatter/controlPanel.ts
similarity index 96%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Scatter/controlPanel.ts
rename to superset-frontend/plugins/preset-chart-deckgl/src/layers/Scatter/controlPanel.ts
index e670bcde231..20a788c82a3 100644
--- a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Scatter/controlPanel.ts
+++ b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Scatter/controlPanel.ts
@@ -34,6 +34,8 @@ import {
   pointRadiusFixed,
   multiplier,
   mapboxStyle,
+  maplibreStyle,
+  mapProvider,
   generateDeckGLColorSchemeControls,
   tooltipContents,
   tooltipTemplate,
@@ -66,7 +68,12 @@ const config: ControlPanelConfig = {
     {
       label: t('Map'),
       expanded: true,
-      controlSetRows: [[mapboxStyle], [autozoom, viewport]],
+      controlSetRows: [
+        [mapProvider],
+        [mapboxStyle],
+        [maplibreStyle],
+        [autozoom, viewport],
+      ],
     },
     {
       label: t('Point Size'),
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Scatter/images/example-dark.png b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Scatter/images/example-dark.png
similarity index 100%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Scatter/images/example-dark.png
rename to superset-frontend/plugins/preset-chart-deckgl/src/layers/Scatter/images/example-dark.png
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Scatter/images/example.png b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Scatter/images/example.png
similarity index 100%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Scatter/images/example.png
rename to superset-frontend/plugins/preset-chart-deckgl/src/layers/Scatter/images/example.png
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Scatter/images/thumbnail-dark.png b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Scatter/images/thumbnail-dark.png
similarity index 100%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Scatter/images/thumbnail-dark.png
rename to superset-frontend/plugins/preset-chart-deckgl/src/layers/Scatter/images/thumbnail-dark.png
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Scatter/images/thumbnail.png b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Scatter/images/thumbnail.png
similarity index 100%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Scatter/images/thumbnail.png
rename to superset-frontend/plugins/preset-chart-deckgl/src/layers/Scatter/images/thumbnail.png
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Scatter/images/thumbnailLarge.png b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Scatter/images/thumbnailLarge.png
similarity index 100%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Scatter/images/thumbnailLarge.png
rename to superset-frontend/plugins/preset-chart-deckgl/src/layers/Scatter/images/thumbnailLarge.png
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Scatter/index.ts b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Scatter/index.ts
similarity index 100%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Scatter/index.ts
rename to superset-frontend/plugins/preset-chart-deckgl/src/layers/Scatter/index.ts
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Scatter/stories/Scatter.stories.tsx b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Scatter/stories/Scatter.stories.tsx
similarity index 93%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Scatter/stories/Scatter.stories.tsx
rename to superset-frontend/plugins/preset-chart-deckgl/src/layers/Scatter/stories/Scatter.stories.tsx
index be9c9a949aa..9bae33a6083 100644
--- a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Scatter/stories/Scatter.stories.tsx
+++ b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Scatter/stories/Scatter.stories.tsx
@@ -20,14 +20,14 @@
 /* eslint-disable sort-keys */
 /* eslint-disable no-magic-numbers */
 import { SuperChart } from '@superset-ui/core';
-import { ScatterChartPlugin } from '@superset-ui/legacy-preset-chart-deckgl';
+import { ScatterChartPlugin } from '@superset-ui/preset-chart-deckgl';
 import { withResizableChartDemo, dummyDatasource } from '@storybook-shared';
 import payload from './payload';
 
 new ScatterChartPlugin().configure({ key: 'deck_scatter' }).register();
 
 export default {
-  title: 'Legacy Chart Plugins/legacy-preset-chart-deckgl/ScatterChartPlugin',
+  title: 'Chart Plugins/preset-chart-deckgl/ScatterChartPlugin',
   decorators: [withResizableChartDemo],
   args: {
     minRadius: 2,
@@ -85,7 +85,8 @@ export const ScatterChartViz = ({
       row_limit: 5000,
       filter_nulls: true,
       adhoc_filters: [],
-      mapbox_style: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png',
+      map_style:
+        'https://basemaps.cartocdn.com/gl/positron-gl-style/style.json',
       viewport: {
         bearing: -4.952916738791771,
         latitude: 37.78926922909199,
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Scatter/stories/payload.ts b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Scatter/stories/payload.ts
similarity index 100%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Scatter/stories/payload.ts
rename to superset-frontend/plugins/preset-chart-deckgl/src/layers/Scatter/stories/payload.ts
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Scatter/transformProps.test.ts b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Scatter/transformProps.test.ts
similarity index 100%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Scatter/transformProps.test.ts
rename to superset-frontend/plugins/preset-chart-deckgl/src/layers/Scatter/transformProps.test.ts
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Scatter/transformProps.ts b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Scatter/transformProps.ts
similarity index 100%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Scatter/transformProps.ts
rename to superset-frontend/plugins/preset-chart-deckgl/src/layers/Scatter/transformProps.ts
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Screengrid/Screengrid.tsx b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Screengrid/Screengrid.tsx
similarity index 97%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Screengrid/Screengrid.tsx
rename to superset-frontend/plugins/preset-chart-deckgl/src/layers/Screengrid/Screengrid.tsx
index ec39564387a..e72b523ceb3 100644
--- a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Screengrid/Screengrid.tsx
+++ b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Screengrid/Screengrid.tsx
@@ -73,10 +73,13 @@ function defaultTooltipGenerator(o: JsonObject, formData: QueryFormData) {
         label={`${metricLabel}: `}
         value={`${o.object?.cellWeight}`}
       />
-      <TooltipRow label="Points: " value={`${pointCount} records`} />
+      <TooltipRow
+        label={`${t('Points')}: `}
+        value={`${pointCount} ${t('records')}`}
+      />
       {points.length > 0 && points.length <= 3 && (
         <div style={{ marginTop: 8, fontSize: '12px' }}>
-          <strong>Records:</strong>
+          <strong>{t('Records')}:</strong>
           {points.slice(0, 3).map((point: JsonObject, index: number) => (
             <div key={index} style={{ marginTop: 4, paddingLeft: '8px' }}>
               {Object.entries(point).map(([key, value]) =>
@@ -95,7 +98,7 @@ function defaultTooltipGenerator(o: JsonObject, formData: QueryFormData) {
       )}
       {points.length > 3 && (
         <MoreRecordsIndicator>
-          ... and {points.length - 3} more records
+          {t('... and %s more records', points.length - 3)}
         </MoreRecordsIndicator>
       )}
     </div>
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Screengrid/buildQuery.ts b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Screengrid/buildQuery.ts
similarity index 100%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Screengrid/buildQuery.ts
rename to superset-frontend/plugins/preset-chart-deckgl/src/layers/Screengrid/buildQuery.ts
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Screengrid/controlPanel.ts b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Screengrid/controlPanel.ts
similarity index 94%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Screengrid/controlPanel.ts
rename to superset-frontend/plugins/preset-chart-deckgl/src/layers/Screengrid/controlPanel.ts
index 22c31047204..feaa07d1c03 100644
--- a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Screengrid/controlPanel.ts
+++ b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Screengrid/controlPanel.ts
@@ -34,6 +34,8 @@ import {
   viewport,
   spatial,
   mapboxStyle,
+  maplibreStyle,
+  mapProvider,
   deckGLFixedColor,
   deckGLCategoricalColorSchemeSelect,
   deckGLCategoricalColorSchemeTypeSelect,
@@ -59,7 +61,12 @@ const config: ControlPanelConfig = {
     },
     {
       label: t('Map'),
-      controlSetRows: [[mapboxStyle], [autozoom, viewport]],
+      controlSetRows: [
+        [mapProvider],
+        [mapboxStyle],
+        [maplibreStyle],
+        [autozoom, viewport],
+      ],
     },
     {
       label: t('Grid'),
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Screengrid/images/example-dark.png b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Screengrid/images/example-dark.png
similarity index 100%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Screengrid/images/example-dark.png
rename to superset-frontend/plugins/preset-chart-deckgl/src/layers/Screengrid/images/example-dark.png
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Screengrid/images/example.png b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Screengrid/images/example.png
similarity index 100%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Screengrid/images/example.png
rename to superset-frontend/plugins/preset-chart-deckgl/src/layers/Screengrid/images/example.png
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Screengrid/images/thumbnail-dark.png b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Screengrid/images/thumbnail-dark.png
similarity index 100%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Screengrid/images/thumbnail-dark.png
rename to superset-frontend/plugins/preset-chart-deckgl/src/layers/Screengrid/images/thumbnail-dark.png
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Screengrid/images/thumbnail.png b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Screengrid/images/thumbnail.png
similarity index 100%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Screengrid/images/thumbnail.png
rename to superset-frontend/plugins/preset-chart-deckgl/src/layers/Screengrid/images/thumbnail.png
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Screengrid/images/thumbnailLarge.png b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Screengrid/images/thumbnailLarge.png
similarity index 100%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Screengrid/images/thumbnailLarge.png
rename to superset-frontend/plugins/preset-chart-deckgl/src/layers/Screengrid/images/thumbnailLarge.png
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Screengrid/index.ts b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Screengrid/index.ts
similarity index 100%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Screengrid/index.ts
rename to superset-frontend/plugins/preset-chart-deckgl/src/layers/Screengrid/index.ts
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Screengrid/stories/Screengrid.stories.tsx b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Screengrid/stories/Screengrid.stories.tsx
similarity index 91%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Screengrid/stories/Screengrid.stories.tsx
rename to superset-frontend/plugins/preset-chart-deckgl/src/layers/Screengrid/stories/Screengrid.stories.tsx
index dc2c53ab597..c3e79295df1 100644
--- a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Screengrid/stories/Screengrid.stories.tsx
+++ b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Screengrid/stories/Screengrid.stories.tsx
@@ -20,15 +20,14 @@
 /* eslint-disable sort-keys */
 /* eslint-disable no-magic-numbers */
 import { SuperChart } from '@superset-ui/core';
-import { ScreengridChartPlugin } from '@superset-ui/legacy-preset-chart-deckgl';
+import { ScreengridChartPlugin } from '@superset-ui/preset-chart-deckgl';
 import { withResizableChartDemo, dummyDatasource } from '@storybook-shared';
 import payload from './payload';
 
 new ScreengridChartPlugin().configure({ key: 'deck_screengrid' }).register();
 
 export default {
-  title:
-    'Legacy Chart Plugins/legacy-preset-chart-deckgl/ScreengridChartPlugin',
+  title: 'Chart Plugins/preset-chart-deckgl/ScreengridChartPlugin',
   decorators: [withResizableChartDemo],
   args: {
     gridSize: 20,
@@ -73,7 +72,8 @@ export const ScreengridChartViz = ({
       row_limit: 5000,
       filter_nulls: true,
       adhoc_filters: [],
-      mapbox_style: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png',
+      map_style:
+        'https://basemaps.cartocdn.com/gl/positron-gl-style/style.json',
       viewport: {
         bearing: -4.952916738791771,
         latitude: 37.76024135844065,
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Screengrid/stories/payload.ts b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Screengrid/stories/payload.ts
similarity index 100%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Screengrid/stories/payload.ts
rename to superset-frontend/plugins/preset-chart-deckgl/src/layers/Screengrid/stories/payload.ts
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Screengrid/transformProps.ts b/superset-frontend/plugins/preset-chart-deckgl/src/layers/Screengrid/transformProps.ts
similarity index 100%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Screengrid/transformProps.ts
rename to superset-frontend/plugins/preset-chart-deckgl/src/layers/Screengrid/transformProps.ts
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/buildQueryUtils.ts b/superset-frontend/plugins/preset-chart-deckgl/src/layers/buildQueryUtils.ts
similarity index 100%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/buildQueryUtils.ts
rename to superset-frontend/plugins/preset-chart-deckgl/src/layers/buildQueryUtils.ts
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/common.test.ts b/superset-frontend/plugins/preset-chart-deckgl/src/layers/common.test.ts
similarity index 100%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/common.test.ts
rename to superset-frontend/plugins/preset-chart-deckgl/src/layers/common.test.ts
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/common.tsx b/superset-frontend/plugins/preset-chart-deckgl/src/layers/common.tsx
similarity index 100%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/common.tsx
rename to superset-frontend/plugins/preset-chart-deckgl/src/layers/common.tsx
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/index.ts b/superset-frontend/plugins/preset-chart-deckgl/src/layers/index.ts
similarity index 100%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/index.ts
rename to superset-frontend/plugins/preset-chart-deckgl/src/layers/index.ts
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/spatialUtils.test.ts b/superset-frontend/plugins/preset-chart-deckgl/src/layers/spatialUtils.test.ts
similarity index 99%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/spatialUtils.test.ts
rename to superset-frontend/plugins/preset-chart-deckgl/src/layers/spatialUtils.test.ts
index b33d2f3c86b..90c9206b637 100644
--- a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/spatialUtils.test.ts
+++ b/superset-frontend/plugins/preset-chart-deckgl/src/layers/spatialUtils.test.ts
@@ -506,7 +506,7 @@ describe('spatialUtils', () => {
     });
 
     expect(result.payload.data.features).toHaveLength(2);
-    expect(result.payload.data.mapboxApiKey).toBe('test_api_key');
+    expect(result.payload.data.mapboxApiKey).toBe('');
     expect(result.payload.data.metricLabels).toEqual(['count_label']);
   });
 
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/spatialUtils.ts b/superset-frontend/plugins/preset-chart-deckgl/src/layers/spatialUtils.ts
similarity index 93%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/spatialUtils.ts
rename to superset-frontend/plugins/preset-chart-deckgl/src/layers/spatialUtils.ts
index 1572c0933b5..82034344e51 100644
--- a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/spatialUtils.ts
+++ b/superset-frontend/plugins/preset-chart-deckgl/src/layers/spatialUtils.ts
@@ -317,26 +317,12 @@ export function processSpatialData(
 
 const NOOP = () => {};
 
-export function getMapboxApiKey(mapboxApiKey?: string): string {
-  if (mapboxApiKey) {
-    return mapboxApiKey;
-  }
-
-  if (typeof document !== 'undefined') {
-    try {
-      const appContainer = document.getElementById('app');
-      const dataBootstrap = appContainer?.getAttribute('data-bootstrap');
-      if (dataBootstrap) {
-        const bootstrapData: BootstrapData = JSON.parse(dataBootstrap);
-        return bootstrapData?.common?.conf?.MAPBOX_API_KEY || '';
-      }
-    } catch (error) {
-      throw new Error(
-        `Failed to read MAPBOX_API_KEY from bootstrap data: ${error}`,
-      );
-    }
-  }
-
+/**
+ * @deprecated MapLibre does not require an API key. This function is kept for
+ * backward compatibility with any code that references `mapboxApiKey` in the
+ * payload data shape. It always returns an empty string.
+ */
+export function getMapboxApiKey(_mapboxApiKey?: string): string {
   return '';
 }
 
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/transformUtils.test.ts b/superset-frontend/plugins/preset-chart-deckgl/src/layers/transformUtils.test.ts
similarity index 100%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/transformUtils.test.ts
rename to superset-frontend/plugins/preset-chart-deckgl/src/layers/transformUtils.test.ts
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/transformUtils.ts b/superset-frontend/plugins/preset-chart-deckgl/src/layers/transformUtils.ts
similarity index 100%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/transformUtils.ts
rename to superset-frontend/plugins/preset-chart-deckgl/src/layers/transformUtils.ts
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/utils/metricUtils.test.ts b/superset-frontend/plugins/preset-chart-deckgl/src/layers/utils/metricUtils.test.ts
similarity index 100%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/utils/metricUtils.test.ts
rename to superset-frontend/plugins/preset-chart-deckgl/src/layers/utils/metricUtils.test.ts
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/utils/metricUtils.ts b/superset-frontend/plugins/preset-chart-deckgl/src/layers/utils/metricUtils.ts
similarity index 100%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/utils/metricUtils.ts
rename to superset-frontend/plugins/preset-chart-deckgl/src/layers/utils/metricUtils.ts
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/preset.ts b/superset-frontend/plugins/preset-chart-deckgl/src/preset.ts
similarity index 100%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/preset.ts
rename to superset-frontend/plugins/preset-chart-deckgl/src/preset.ts
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/transformProps.ts b/superset-frontend/plugins/preset-chart-deckgl/src/transformProps.ts
similarity index 100%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/transformProps.ts
rename to superset-frontend/plugins/preset-chart-deckgl/src/transformProps.ts
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/types.ts b/superset-frontend/plugins/preset-chart-deckgl/src/types.ts
similarity index 100%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/types.ts
rename to superset-frontend/plugins/preset-chart-deckgl/src/types.ts
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/utilities/HandlebarsRenderer.tsx b/superset-frontend/plugins/preset-chart-deckgl/src/utilities/HandlebarsRenderer.tsx
similarity index 100%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/utilities/HandlebarsRenderer.tsx
rename to superset-frontend/plugins/preset-chart-deckgl/src/utilities/HandlebarsRenderer.tsx
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/utilities/Shared_DeckGL.tsx b/superset-frontend/plugins/preset-chart-deckgl/src/utilities/Shared_DeckGL.tsx
similarity index 90%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/utilities/Shared_DeckGL.tsx
rename to superset-frontend/plugins/preset-chart-deckgl/src/utilities/Shared_DeckGL.tsx
index b2aabf4d931..490e1367be4 100644
--- a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/utilities/Shared_DeckGL.tsx
+++ b/superset-frontend/plugins/preset-chart-deckgl/src/utilities/Shared_DeckGL.tsx
@@ -22,7 +22,6 @@ import {
   FeatureFlag,
   isFeatureEnabled,
   validateNonEmpty,
-  validateMapboxStylesUrl,
   getCategoricalSchemeRegistry,
   getSequentialSchemeRegistry,
   SequentialScheme,
@@ -50,12 +49,22 @@ export const DEFAULT_DECKGL_COLOR = { r: 158, g: 158, b: 158, a: 1 };
 let deckglTiles: string[][];
 
 export const DEFAULT_DECKGL_TILES = [
-  ['https://tile.openstreetmap.org/{z}/{x}/{y}.png', 'Streets (OSM)'],
-  ['https://tile.osm.ch/osm-swiss-style/{z}/{x}/{y}.png', 'Topography (OSM)'],
   [
-    'tile://https://a.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}.png',
-    'Dark (OSM)',
+    'https://basemaps.cartocdn.com/gl/positron-gl-style/style.json',
+    'Light (Carto)',
   ],
+  [
+    'https://basemaps.cartocdn.com/gl/dark-matter-gl-style/style.json',
+    'Dark (Carto)',
+  ],
+  [
+    'https://basemaps.cartocdn.com/gl/voyager-gl-style/style.json',
+    'Streets (Carto)',
+  ],
+  ['https://tiles.openfreemap.org/styles/liberty', 'Liberty (OpenFreeMap)'],
+];
+
+export const DEFAULT_MAPBOX_TILES = [
   ['mapbox://styles/mapbox/streets-v9', 'Streets (Mapbox)'],
   ['mapbox://styles/mapbox/dark-v9', 'Dark (Mapbox)'],
   ['mapbox://styles/mapbox/light-v9', 'Light (Mapbox)'],
@@ -85,7 +94,7 @@ const DEFAULT_VIEWPORT = {
 
 const sandboxUrl =
   'https://github.com/apache/superset/' +
-  'blob/master/superset-frontend/plugins/legacy-preset-chart-deckgl/src/utils/sandbox.ts';
+  'blob/master/superset-frontend/plugins/preset-chart-deckgl/src/utils/sandbox.ts';
 const jsFunctionInfo = (
   <div>
     {t(
@@ -409,22 +418,58 @@ export const reverseLongLat = {
   },
 };
 
-export const mapboxStyle = {
-  name: 'mapbox_style',
+export const mapProvider = {
+  name: 'map_renderer',
+  config: {
+    type: 'SelectControl',
+    label: t('Map Renderer'),
+    clearable: false,
+    renderTrigger: true,
+    choices: [
+      ['maplibre', t('MapLibre (open-source)')],
+      ['mapbox', t('Mapbox (API key required)')],
+    ],
+    default: 'maplibre',
+    description: t(
+      'Select the map tile provider. MapLibre is open-source and requires no API key. ' +
+        'Mapbox requires MAPBOX_API_KEY to be configured in Superset.',
+    ),
+  },
+};
+
+export const maplibreStyle = {
+  name: 'maplibre_style',
   config: {
     type: 'SelectControl',
     label: t('Map Style'),
     clearable: false,
     renderTrigger: true,
     freeForm: true,
-    validators: [validateMapboxStylesUrl],
     choices: getDeckGLTiles(),
     default: getDeckGLTiles()[0][0],
     description: t(
-      'Base layer map style. See Mapbox documentation: %s',
-      'Mapbox base layer map style (see Mapbox documentation: %s) or tile server URL.',
-      'https://docs.mapbox.com/help/glossary/style-url/',
+      'Base layer map style. Accepts a MapLibre-compatible style URL.',
+    ),
+    visibility: ({ controls }: ControlPanelState) =>
+      controls?.map_renderer?.value !== 'mapbox',
+  },
+};
+
+export const mapboxStyle = {
+  name: 'mapbox_style',
+  config: {
+    type: 'SelectControl',
+    label: t('Map Style'),
+    clearable: false,
+    renderTrigger: true,
+    freeForm: true,
+    choices: DEFAULT_MAPBOX_TILES,
+    default: DEFAULT_MAPBOX_TILES[0][0],
+    description: t(
+      'Base layer map style. Accepts a Mapbox style URL (mapbox://styles/...).',
     ),
+    visibility: ({ controls }: ControlPanelState) =>
+      controls?.map_renderer?.value === 'mapbox',
   },
 };
 
@@ -508,7 +553,7 @@ export const tooltipTemplate = {
     default: '',
     description: '',
     placeholder: '',
-    mapStateToProps: (state: any, control: any) => ({
+    mapStateToProps: (_state: any, control: any) => ({
       value: control.value,
     }),
   },
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/utilities/TooltipTemplateControl.tsx b/superset-frontend/plugins/preset-chart-deckgl/src/utilities/TooltipTemplateControl.tsx
similarity index 100%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/utilities/TooltipTemplateControl.tsx
rename to superset-frontend/plugins/preset-chart-deckgl/src/utilities/TooltipTemplateControl.tsx
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/utilities/TooltipTemplateEditor.tsx b/superset-frontend/plugins/preset-chart-deckgl/src/utilities/TooltipTemplateEditor.tsx
similarity index 100%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/utilities/TooltipTemplateEditor.tsx
rename to superset-frontend/plugins/preset-chart-deckgl/src/utilities/TooltipTemplateEditor.tsx
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/utilities/controlRegistry.tsx b/superset-frontend/plugins/preset-chart-deckgl/src/utilities/controlRegistry.tsx
similarity index 100%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/utilities/controlRegistry.tsx
rename to superset-frontend/plugins/preset-chart-deckgl/src/utilities/controlRegistry.tsx
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/utilities/controls.ts b/superset-frontend/plugins/preset-chart-deckgl/src/utilities/controls.ts
similarity index 100%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/utilities/controls.ts
rename to superset-frontend/plugins/preset-chart-deckgl/src/utilities/controls.ts
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/utilities/multiValueUtils.ts b/superset-frontend/plugins/preset-chart-deckgl/src/utilities/multiValueUtils.ts
similarity index 100%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/utilities/multiValueUtils.ts
rename to superset-frontend/plugins/preset-chart-deckgl/src/utilities/multiValueUtils.ts
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/utilities/sharedDndControls.tsx b/superset-frontend/plugins/preset-chart-deckgl/src/utilities/sharedDndControls.tsx
similarity index 100%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/utilities/sharedDndControls.tsx
rename to superset-frontend/plugins/preset-chart-deckgl/src/utilities/sharedDndControls.tsx
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/utilities/tooltipUtils.tsx b/superset-frontend/plugins/preset-chart-deckgl/src/utilities/tooltipUtils.tsx
similarity index 100%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/utilities/tooltipUtils.tsx
rename to superset-frontend/plugins/preset-chart-deckgl/src/utilities/tooltipUtils.tsx
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/utilities/utils.ts b/superset-frontend/plugins/preset-chart-deckgl/src/utilities/utils.ts
similarity index 100%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/utilities/utils.ts
rename to superset-frontend/plugins/preset-chart-deckgl/src/utilities/utils.ts
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/utils.test.ts b/superset-frontend/plugins/preset-chart-deckgl/src/utils.test.ts
similarity index 100%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/utils.test.ts
rename to superset-frontend/plugins/preset-chart-deckgl/src/utils.test.ts
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/utils.ts b/superset-frontend/plugins/preset-chart-deckgl/src/utils.ts
similarity index 86%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/utils.ts
rename to superset-frontend/plugins/preset-chart-deckgl/src/utils.ts
index 6927e6306d7..2c42b918b7f 100644
--- a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/utils.ts
+++ b/superset-frontend/plugins/preset-chart-deckgl/src/utils.ts
@@ -26,8 +26,6 @@ import {
   SequentialScheme,
 } from '@superset-ui/core';
 import { Color } from '@deck.gl/core';
-import { GeoBoundingBox, TileLayer } from '@deck.gl/geo-layers';
-import { BitmapLayer, PathLayer } from '@deck.gl/layers';
 import { hexToRGB } from './utils/colors';
 import { ColorBreakpointType } from './types';
 
@@ -36,10 +34,6 @@ export const HIGHLIGHT_COLOR_ARRAY = [255, 0, 0, 255] as Color;
 
 const DEFAULT_NUM_BUCKETS = 10;
 
-export const MAPBOX_LAYER_PREFIX = 'mapbox://';
-export const TILE_LAYER_PREFIX = 'tile://';
-export const OSM_LAYER_KEYWORDS = ['openstreetmap', 'osm'];
-
 export type Buckets = {
   break_points: string[];
   num_buckets: string;
@@ -243,42 +237,3 @@ export function getColorBreakpointsBuckets(
 
   return buckets;
 }
-
-export function buildTileLayer(url: string, id: string) {
-  interface TileLayerProps {
-    id: string;
-    data: string;
-    minZoom: number;
-    maxZoom: number;
-    tileSize: number;
-    renderSubLayers: (props: any) => (BitmapLayer | PathLayer)[];
-  }
-
-  interface RenderSubLayerProps {
-    tile: {
-      bbox: GeoBoundingBox;
-    };
-    data: any;
-  }
-
-  return new TileLayer({
-    data: url,
-    id,
-    minZoom: 0,
-    maxZoom: 19,
-    tileSize: 256,
-
-    renderSubLayers: (props: RenderSubLayerProps): BitmapLayer[] => {
-      const { west, north, east, south } = props.tile.bbox as GeoBoundingBox;
-
-      // Ajouter une BitmapLayer
-      const bitmapLayer = new BitmapLayer(props, {
-        data: undefined,
-        image: props.data,
-        bounds: [west, south, east, north],
-      });
-
-      return [bitmapLayer];
-    },
-  } as TileLayerProps);
-}
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/utils/colors.ts b/superset-frontend/plugins/preset-chart-deckgl/src/utils/colors.ts
similarity index 100%
copy from superset-frontend/plugins/legacy-preset-chart-deckgl/src/utils/colors.ts
copy to superset-frontend/plugins/preset-chart-deckgl/src/utils/colors.ts
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/utils/computeBoundsFromPoints.ts b/superset-frontend/plugins/preset-chart-deckgl/src/utils/computeBoundsFromPoints.ts
similarity index 100%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/utils/computeBoundsFromPoints.ts
rename to superset-frontend/plugins/preset-chart-deckgl/src/utils/computeBoundsFromPoints.ts
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/utils/crossFiltersDataMask.test.ts b/superset-frontend/plugins/preset-chart-deckgl/src/utils/crossFiltersDataMask.test.ts
similarity index 100%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/utils/crossFiltersDataMask.test.ts
rename to superset-frontend/plugins/preset-chart-deckgl/src/utils/crossFiltersDataMask.test.ts
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/utils/crossFiltersDataMask.ts b/superset-frontend/plugins/preset-chart-deckgl/src/utils/crossFiltersDataMask.ts
similarity index 100%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/utils/crossFiltersDataMask.ts
rename to superset-frontend/plugins/preset-chart-deckgl/src/utils/crossFiltersDataMask.ts
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/utils/explore.ts b/superset-frontend/plugins/preset-chart-deckgl/src/utils/explore.ts
similarity index 100%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/utils/explore.ts
rename to superset-frontend/plugins/preset-chart-deckgl/src/utils/explore.ts
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/utils/fitViewport.ts b/superset-frontend/plugins/preset-chart-deckgl/src/utils/fitViewport.ts
similarity index 100%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/utils/fitViewport.ts
rename to superset-frontend/plugins/preset-chart-deckgl/src/utils/fitViewport.ts
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/utils/geo.ts b/superset-frontend/plugins/preset-chart-deckgl/src/utils/geo.ts
similarity index 100%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/utils/geo.ts
rename to superset-frontend/plugins/preset-chart-deckgl/src/utils/geo.ts
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/utils/getPointsFromPolygon.ts b/superset-frontend/plugins/preset-chart-deckgl/src/utils/getPointsFromPolygon.ts
similarity index 100%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/utils/getPointsFromPolygon.ts
rename to superset-frontend/plugins/preset-chart-deckgl/src/utils/getPointsFromPolygon.ts
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/utils/colors.ts b/superset-frontend/plugins/preset-chart-deckgl/src/utils/mapbox.ts
similarity index 62%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/utils/colors.ts
rename to superset-frontend/plugins/preset-chart-deckgl/src/utils/mapbox.ts
index 7f562dbca23..b617b4c37cc 100644
--- a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/utils/colors.ts
+++ b/superset-frontend/plugins/preset-chart-deckgl/src/utils/mapbox.ts
@@ -16,17 +16,20 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-import { rgb } from 'd3-color';
 
-// eslint-disable-next-line import/prefer-default-export
-export function hexToRGB(
-  hex: string | undefined,
-  alpha = 255,
-): [number, number, number, number] {
-  if (!hex) {
-    return [0, 0, 0, alpha];
+export function getMapboxApiKey(): string {
+  if (typeof document === 'undefined') {
+    return '';
   }
-  const { r, g, b } = rgb(hex);
-
-  return [r, g, b, alpha];
+  try {
+    const appContainer = document.getElementById('app');
+    const dataBootstrap = appContainer?.getAttribute('data-bootstrap');
+    if (dataBootstrap) {
+      const bootstrapData = JSON.parse(dataBootstrap);
+      return bootstrapData?.common?.conf?.MAPBOX_API_KEY || '';
+    }
+  } catch {
+    // If bootstrap data is unavailable or malformed, return empty string
+  }
+  return '';
 }
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/utils/safeStringify.ts b/superset-frontend/plugins/preset-chart-deckgl/src/utils/safeStringify.ts
similarity index 100%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/utils/safeStringify.ts
rename to superset-frontend/plugins/preset-chart-deckgl/src/utils/safeStringify.ts
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/utils/sandbox.ts b/superset-frontend/plugins/preset-chart-deckgl/src/utils/sandbox.ts
similarity index 100%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/src/utils/sandbox.ts
rename to superset-frontend/plugins/preset-chart-deckgl/src/utils/sandbox.ts
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/tsconfig.json b/superset-frontend/plugins/preset-chart-deckgl/tsconfig.json
similarity index 58%
rename from superset-frontend/plugins/legacy-preset-chart-deckgl/tsconfig.json
rename to superset-frontend/plugins/preset-chart-deckgl/tsconfig.json
index e73ca5d4f87..1abdcd42c58 100644
--- a/superset-frontend/plugins/legacy-preset-chart-deckgl/tsconfig.json
+++ b/superset-frontend/plugins/preset-chart-deckgl/tsconfig.json
@@ -1,12 +1,7 @@
 {
   "extends": "../../tsconfig.json",
   "compilerOptions": {
-    // Path Resolution: Override baseUrl to maintain correct path mappings from parent config
-    // (e.g., "@apache-superset/core" -> "./packages/superset-core/src")
     "baseUrl": "../..",
-
-    // Directory Overrides: Parent config paths are relative to frontend root,
-    // but packages need paths relative to their own directory
     "outDir": "lib",
     "rootDir": "src",
     "declarationDir": "lib"
diff --git a/superset-frontend/plugins/legacy-plugin-chart-map-box/src/MapBox.css b/superset-frontend/plugins/preset-chart-deckgl/types/external.d.ts
similarity index 84%
rename from superset-frontend/plugins/legacy-plugin-chart-map-box/src/MapBox.css
rename to superset-frontend/plugins/preset-chart-deckgl/types/external.d.ts
index adade29f589..b904c352de4 100644
--- a/superset-frontend/plugins/legacy-plugin-chart-map-box/src/MapBox.css
+++ b/superset-frontend/plugins/preset-chart-deckgl/types/external.d.ts
@@ -1,4 +1,4 @@
-/**
+/*
  * 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
@@ -16,6 +16,13 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-.mapbox .slice_container div {
-  padding-top: 0px;
+
+declare module '*.png' {
+  const value: string;
+  export default value;
+}
+
+declare module '*.jpg' {
+  const value: string;
+  export default value;
 }
diff --git a/superset-frontend/scripts/check-custom-rules.js b/superset-frontend/scripts/check-custom-rules.js
index ebd9d0a1f83..e9c6eaca924 100755
--- a/superset-frontend/scripts/check-custom-rules.js
+++ b/superset-frontend/scripts/check-custom-rules.js
@@ -600,6 +600,7 @@ function main() {
     /\/lib\//,
     /\/dist\//,
     /plugins\/legacy-/, // Legacy plugins can have old color patterns
+    /plugin-chart-point-cluster-map\/src\/controlPanel/, // Data visualization color choices
     /\/vendor\//, // Third-party vendor code
     /spec\/fixtures\//, // Test fixtures
     /theme\/exampleThemes/, // Theme examples legitimately have colors
@@ -628,6 +629,7 @@ function main() {
         '**/lib/**', // Build artifacts
         '**/dist/**', // Build artifacts
         'plugins/legacy-*/**', // Legacy plugins
+        'plugins/plugin-chart-point-cluster-map/src/controlPanel.*', // Data visualization color choices
         '**/vendor/**',
         'spec/fixtures/**',
         '**/theme/exampleThemes/**',
diff --git a/superset-frontend/src/explore/components/controls/VizTypeControl/VizTypeControl.test.tsx b/superset-frontend/src/explore/components/controls/VizTypeControl/VizTypeControl.test.tsx
index f5033f49966..e35619772c2 100644
--- a/superset-frontend/src/explore/components/controls/VizTypeControl/VizTypeControl.test.tsx
+++ b/superset-frontend/src/explore/components/controls/VizTypeControl/VizTypeControl.test.tsx
@@ -39,7 +39,7 @@ import {
   EchartsTimeseriesLineChartPlugin,
 } from '../../../../../plugins/plugin-chart-echarts/src';
 import TableChartPlugin from '../../../../../plugins/plugin-chart-table/src';
-import { MultiChartPlugin } from '../../../../../plugins/legacy-preset-chart-deckgl/src';
+import { MultiChartPlugin } from '../../../../../plugins/preset-chart-deckgl/src';
 import VizTypeControl, { VIZ_TYPE_CONTROL_TEST_ID } from './index';
 
 // Mock scrollIntoView to avoid errors in test environment
diff --git a/superset-frontend/src/visualizations/presets/MainPreset.ts b/superset-frontend/src/visualizations/presets/MainPreset.ts
index c28dea34945..d1a12de0fb4 100644
--- a/superset-frontend/src/visualizations/presets/MainPreset.ts
+++ b/superset-frontend/src/visualizations/presets/MainPreset.ts
@@ -26,7 +26,6 @@ import CalendarChartPlugin from '@superset-ui/legacy-plugin-chart-calendar';
 import ChordChartPlugin from '@superset-ui/legacy-plugin-chart-chord';
 import CountryMapChartPlugin from '@superset-ui/legacy-plugin-chart-country-map';
 import HorizonChartPlugin from '@superset-ui/legacy-plugin-chart-horizon';
-import MapBoxChartPlugin from '@superset-ui/legacy-plugin-chart-map-box';
 import PairedTTestChartPlugin from '@superset-ui/legacy-plugin-chart-paired-t-test';
 import ParallelCoordinatesChartPlugin from '@superset-ui/legacy-plugin-chart-parallel-coordinates';
 import PartitionChartPlugin from '@superset-ui/legacy-plugin-chart-partition';
@@ -40,7 +39,8 @@ import {
   CompareChartPlugin,
   TimePivotChartPlugin,
 } from '@superset-ui/legacy-preset-chart-nvd3';
-import { DeckGLChartPreset } from '@superset-ui/legacy-preset-chart-deckgl';
+import { DeckGLChartPreset } from '@superset-ui/preset-chart-deckgl';
+import ScatterMapChartPlugin from '@superset-ui/plugin-chart-point-cluster-map';
 import { CartodiagramPlugin } from '@superset-ui/plugin-chart-cartodiagram';
 import {
   BigNumberChartPlugin,
@@ -131,7 +131,7 @@ export default class MainPreset extends Preset {
           key: VizType.MixedTimeseries,
         }),
         new HorizonChartPlugin().configure({ key: VizType.Horizon }),
-        new MapBoxChartPlugin().configure({ key: VizType.MapBox }),
+        new ScatterMapChartPlugin().configure({ key: VizType.PointClusterMap }),
         new PairedTTestChartPlugin().configure({ key: VizType.PairedTTest }),
         new ParallelCoordinatesChartPlugin().configure({
           key: VizType.ParallelCoordinates,
diff --git a/superset-frontend/tsconfig.json b/superset-frontend/tsconfig.json
index 9097f8a5082..6a05ebca0d6 100644
--- a/superset-frontend/tsconfig.json
+++ b/superset-frontend/tsconfig.json
@@ -83,17 +83,17 @@
     { "path": "./plugins/legacy-plugin-chart-chord" },
     { "path": "./plugins/legacy-plugin-chart-country-map" },
     { "path": "./plugins/legacy-plugin-chart-horizon" },
-    { "path": "./plugins/legacy-plugin-chart-map-box" },
     { "path": "./plugins/legacy-plugin-chart-paired-t-test" },
     { "path": "./plugins/legacy-plugin-chart-parallel-coordinates" },
     { "path": "./plugins/legacy-plugin-chart-partition" },
     { "path": "./plugins/legacy-plugin-chart-rose" },
     { "path": "./plugins/legacy-plugin-chart-world-map" },
-    { "path": "./plugins/legacy-preset-chart-deckgl" },
     { "path": "./plugins/legacy-preset-chart-nvd3" },
     { "path": "./plugins/plugin-chart-ag-grid-table" },
     { "path": "./plugins/plugin-chart-cartodiagram" },
+    { "path": "./plugins/preset-chart-deckgl" },
     { "path": "./plugins/plugin-chart-echarts" },
+    { "path": "./plugins/plugin-chart-point-cluster-map" },
     { "path": "./plugins/plugin-chart-handlebars" },
     { "path": "./plugins/plugin-chart-pivot-table" },
     { "path": "./plugins/plugin-chart-table" },
diff --git a/superset/config.py b/superset/config.py
index 0d9c2ed2e67..0dd47e1d0c3 100644
--- a/superset/config.py
+++ b/superset/config.py
@@ -2153,7 +2153,14 @@ TALISMAN_CONFIG = {
             "https://events.mapbox.com",
             "https://tile.openstreetmap.org",
             "https://tile.osm.ch",
-            "https://a.basemaps.cartocdn.com",
+            "https://basemaps.cartocdn.com",
+            "https://*.basemaps.cartocdn.com",
+            "https://tiles.openfreemap.org",
+            "https://*.maptiler.com",
+            "https://tiles.stadiamaps.com",
+            "https://tiles.versatiles.org",
+            "https://*.protomaps.com",
+            "https://*.maplibre.org",
         ],
         "object-src": "'none'",
         "style-src": [
@@ -2193,7 +2200,14 @@ TALISMAN_DEV_CONFIG = {
             "https://events.mapbox.com",
             "https://tile.openstreetmap.org",
             "https://tile.osm.ch",
-            "https://a.basemaps.cartocdn.com",
+            "https://basemaps.cartocdn.com",
+            "https://*.basemaps.cartocdn.com",
+            "https://tiles.openfreemap.org",
+            "https://*.maptiler.com",
+            "https://tiles.stadiamaps.com",
+            "https://tiles.versatiles.org",
+            "https://*.protomaps.com",
+            "https://*.maplibre.org",
         ],
         "object-src": "'none'",
         "style-src": [
diff --git a/superset/migrations/versions/2026-03-02_00-00_ce6bd21901ab_migrate_deckgl_and_mapbox.py b/superset/migrations/versions/2026-03-02_00-00_ce6bd21901ab_migrate_deckgl_and_mapbox.py
new file mode 100644
index 00000000000..ac6011e3965
--- /dev/null
+++ b/superset/migrations/versions/2026-03-02_00-00_ce6bd21901ab_migrate_deckgl_and_mapbox.py
@@ -0,0 +1,172 @@
+# 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.
+"""migrate mapbox and deckgl charts to point_cluster_map
+
+Revision ID: ce6bd21901ab
+Revises: 4b2a8c9d3e1f
+Create Date: 2026-03-02 00:00:00.000000
+
+
+"""
+
+import copy
+import logging
+from typing import Any
+
+from alembic import op
+from sqlalchemy.orm import Session
+
+from superset import db
+from superset.migrations.shared.migrate_viz.base import (
+    FORM_DATA_BAK_FIELD_NAME,
+    MigrateViz,
+    QUERIES_BAK_FIELD_NAME,
+    Slice,
+)
+from superset.migrations.shared.utils import paginated_update, try_load_json
+from superset.utils import json
+
+logger = logging.getLogger("alembic.env")
+
+# revision identifiers, used by Alembic.
+revision = "ce6bd21901ab"
+down_revision = "a1b2c3d4e5f6"
+
+DECKGL_VIZ_TYPES = [
+    "deck_arc",
+    "deck_contour",
+    "deck_geojson",
+    "deck_grid",
+    "deck_heatmap",
+    "deck_hex",
+    "deck_multi",
+    "deck_path",
+    "deck_polygon",
+    "deck_scatter",
+    "deck_screengrid",
+]
+
+
+class MigrateMapBox(MigrateViz):
+    """Migrate the legacy standalone Mapbox scatter chart to point_cluster_map."""
+
+    has_x_axis_control = False
+    source_viz_type = "mapbox"
+    target_viz_type = "point_cluster_map"
+    rename_keys = {
+        "mapbox_label": "map_label",
+        "mapbox_color": "map_color",
+    }
+    remove_keys = set()
+
+    def _post_action(self) -> None:
+        # If the style URL is a mapbox:// URL, the chart was using Mapbox GL.
+        # Set map_renderer so the new chart continues to use the Mapbox renderer,
+        # which will pick up MAPBOX_API_KEY from the server config.
+        mapbox_style = self.data.get("mapbox_style", "")
+        if isinstance(mapbox_style, str) and mapbox_style.startswith("mapbox://"):
+            self.data["map_renderer"] = "mapbox"
+
+    @classmethod
+    def upgrade_slice(cls, slc: Slice) -> None:
+        try:
+            clz = cls(slc.params)
+            form_data_bak = copy.deepcopy(clz.data)
+
+            clz._pre_action()
+            clz._migrate()
+            clz._post_action()
+
+            slc.viz_type = clz.target_viz_type
+
+            backup: Any | dict[str, Any] = {FORM_DATA_BAK_FIELD_NAME: form_data_bak}
+
+            query_context = try_load_json(slc.query_context)
+            queries_bak = None
+
+            if query_context:
+                if "form_data" in query_context:
+                    query_context["form_data"] = clz.data
+                queries_bak = copy.deepcopy(query_context.get("queries"))
+            else:
+                query_context = {}
+
+            slc.query_context = json.dumps(query_context)
+            if queries_bak is not None:
+                backup[QUERIES_BAK_FIELD_NAME] = queries_bak
+
+            slc.params = json.dumps({**clz.data, **backup})
+
+        except (ValueError, KeyError, TypeError) as e:
+            logger.warning("Failed to migrate slice %s: %s", slc.id, e)
+
+
+def _migrate_deckgl_slice(slc: Slice) -> bool:
+    """Set map_renderer='mapbox' for all existing deck.gl slices.
+
+    This ensures full backwards compatibility: existing charts keep using the
+    Mapbox renderer. Users can later switch to MapLibre in the chart controls.
+    Only new charts will default to MapLibre.
+
+    Returns True if the slice was modified.
+    """
+    params = try_load_json(slc.params)
+    if not params:
+        return False
+
+    if "map_renderer" in params:
+        return False
+
+    params["map_renderer"] = "mapbox"
+    slc.params = json.dumps(params)
+    return True
+
+
+def _downgrade_deckgl_slice(slc: Slice) -> bool:
+    """Reverse _migrate_deckgl_slice. Returns True if the slice was modified."""
+    params = try_load_json(slc.params)
+    if not params or "map_renderer" not in params:
+        return False
+
+    params.pop("map_renderer", None)
+    slc.params = json.dumps(params)
+    return True
+
+
+def _migrate_deckgl_slices(session: Session, *, upgrade: bool) -> None:
+    fn = _migrate_deckgl_slice if upgrade else _downgrade_deckgl_slice
+    query = session.query(Slice).filter(Slice.viz_type.in_(DECKGL_VIZ_TYPES))
+    for slc in paginated_update(query):
+        try:
+            fn(slc)
+        except (ValueError, KeyError, TypeError) as e:
+            logger.warning("Failed to migrate deck.gl slice %s: %s", slc.id, e)
+    session.commit()
+
+
+def upgrade() -> None:
+    bind = op.get_bind()
+    session = db.Session(bind=bind)
+    MigrateMapBox.upgrade(session)
+    _migrate_deckgl_slices(session, upgrade=True)
+
+
+def downgrade() -> None:
+    bind = op.get_bind()
+    session = db.Session(bind=bind)
+    MigrateMapBox.downgrade(session)
+    _migrate_deckgl_slices(session, upgrade=False)
diff --git a/superset/viz.py b/superset/viz.py
index 9bde6d38254..ab5f3f03458 100644
--- a/superset/viz.py
+++ b/superset/viz.py
@@ -1521,6 +1521,29 @@ class MapboxViz(BaseViz):
         }
 
 
+class MapLibreViz(MapboxViz):
+    """Rich maps made with MapLibre"""
+
+    viz_type = "point_cluster_map"
+    verbose_name = _("Point Cluster Map")
+    credits = '<a href="https://maplibre.org/">MapLibre GL JS</a>'
+
+    @deprecated(deprecated_in="3.0")
+    def query_obj(self) -> QueryObjectDict:
+        self.form_data["mapbox_label"] = self.form_data.get("map_label")
+        return super().query_obj()
+
+    @deprecated(deprecated_in="3.0")
+    def get_data(self, df: pd.DataFrame) -> VizData:
+        self.form_data["mapbox_label"] = self.form_data.get("map_label")
+        self.form_data["mapbox_style"] = self.form_data.get("map_style")
+        self.form_data["mapbox_color"] = self.form_data.get("map_color")
+        data = super().get_data(df)
+        if data:
+            data.pop("mapboxApiKey", None)
+        return data
+
+
 class DeckGLMultiLayer(BaseViz):
     """Pile on multiple DeckGL layers"""
 
diff --git a/tests/integration_tests/migrations/ce6bd21901ab_migrate_deckgl_and_mapbox__test.py b/tests/integration_tests/migrations/ce6bd21901ab_migrate_deckgl_and_mapbox__test.py
new file mode 100644
index 00000000000..ce1e73cd70f
--- /dev/null
+++ b/tests/integration_tests/migrations/ce6bd21901ab_migrate_deckgl_and_mapbox__test.py
@@ -0,0 +1,187 @@
+# 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.
+from importlib import import_module
+
+import pytest
+
+from superset.utils import json
+
+migrate_deckgl_and_mapbox = import_module(
+    "superset.migrations.versions."
+    "2026-03-02_00-00_ce6bd21901ab_migrate_deckgl_and_mapbox",
+)
+
+Slice = migrate_deckgl_and_mapbox.Slice
+MigrateMapBox = migrate_deckgl_and_mapbox.MigrateMapBox
+_migrate_deckgl_slice = migrate_deckgl_and_mapbox._migrate_deckgl_slice
+_downgrade_deckgl_slice = migrate_deckgl_and_mapbox._downgrade_deckgl_slice
+
+
+@pytest.mark.usefixtures("app_context")
+def test_upgrade_mapbox():
+    slc = Slice(
+        slice_name="Test Mapbox",
+        viz_type="mapbox",
+        params=json.dumps(
+            {
+                "viz_type": "mapbox",
+                "mapbox_style": "mapbox://styles/mapbox/streets-v11",
+                "mapbox_label": ["name"],
+                "mapbox_color": "#ff0000",
+                "other_param": "value",
+            }
+        ),
+        query_context=json.dumps(
+            {
+                "form_data": {
+                    "viz_type": "mapbox",
+                    "mapbox_style": "mapbox://styles/mapbox/streets-v11",
+                }
+            }
+        ),
+    )
+
+    MigrateMapBox.upgrade_slice(slc)
+
+    assert slc.viz_type == "point_cluster_map"
+
+    params = json.loads(slc.params)
+    assert params["viz_type"] == "point_cluster_map"
+    assert params["mapbox_style"] == "mapbox://styles/mapbox/streets-v11"
+    assert params["map_renderer"] == "mapbox"
+    assert params["map_label"] == ["name"]
+    assert params["map_color"] == "#ff0000"
+    assert "mapbox_label" not in params
+    assert "mapbox_color" not in params
+    assert params["other_param"] == "value"
+
+    query_context = json.loads(slc.query_context)
+    assert query_context["form_data"]["viz_type"] == "point_cluster_map"
+    assert (
+        query_context["form_data"]["mapbox_style"]
+        == "mapbox://styles/mapbox/streets-v11"
+    )
+
+
+@pytest.mark.usefixtures("app_context")
+def test_upgrade_mapbox_with_non_mapbox_style():
+    """Charts with non-mapbox:// style URLs should not get map_provider=mapbox."""
+    slc = Slice(
+        slice_name="Test Mapbox Open Style",
+        viz_type="mapbox",
+        params=json.dumps(
+            {
+                "viz_type": "mapbox",
+                "mapbox_style": "https://tiles.openfreemap.org/styles/liberty",
+                "other_param": "value",
+            }
+        ),
+        query_context=json.dumps({}),
+    )
+
+    MigrateMapBox.upgrade_slice(slc)
+
+    assert slc.viz_type == "point_cluster_map"
+    params = json.loads(slc.params)
+    assert params["mapbox_style"] == "https://tiles.openfreemap.org/styles/liberty"
+    assert "map_renderer" not in params
+
+
+def test_migrate_deckgl_slice_mapbox_style():
+    slc = Slice(
+        slice_name="Test Arc",
+        viz_type="deck_arc",
+        params=json.dumps(
+            {
+                "viz_type": "deck_arc",
+                "mapbox_style": "mapbox://styles/mapbox/dark-v9",
+                "other_param": "value",
+            }
+        ),
+    )
+
+    modified = _migrate_deckgl_slice(slc)
+
+    assert modified is True
+    params = json.loads(slc.params)
+    assert params["mapbox_style"] == "mapbox://styles/mapbox/dark-v9"
+    assert params["map_renderer"] == "mapbox"
+    assert params["viz_type"] == "deck_arc"  # viz_type unchanged
+    assert params["other_param"] == "value"
+
+
+def test_migrate_deckgl_slice_open_style():
+    """All existing deck_* charts get map_renderer='mapbox' for backwards compat."""
+    slc = Slice(
+        slice_name="Test Scatter",
+        viz_type="deck_scatter",
+        params=json.dumps(
+            {
+                "viz_type": "deck_scatter",
+                "mapbox_style": "https://basemaps.cartocdn.com/gl/positron-gl-style/style.json",
+            }
+        ),
+    )
+
+    modified = _migrate_deckgl_slice(slc)
+
+    assert modified is True
+    params = json.loads(slc.params)
+    assert (
+        params["mapbox_style"]
+        == "https://basemaps.cartocdn.com/gl/positron-gl-style/style.json"
+    )
+    assert params["map_renderer"] == "mapbox"
+
+
+def test_migrate_deckgl_slice_no_mapbox_style():
+    """Slices without mapbox_style still get map_renderer='mapbox'."""
+    slc = Slice(
+        slice_name="Test Arc No Style",
+        viz_type="deck_arc",
+        params=json.dumps({"viz_type": "deck_arc", "other_param": "value"}),
+    )
+
+    modified = _migrate_deckgl_slice(slc)
+
+    assert modified is True
+    params = json.loads(slc.params)
+    assert params["map_renderer"] == "mapbox"
+    assert params["other_param"] == "value"
+
+
+def test_downgrade_deckgl_slice():
+    slc = Slice(
+        slice_name="Test Arc",
+        viz_type="deck_arc",
+        params=json.dumps(
+            {
+                "viz_type": "deck_arc",
+                "mapbox_style": "mapbox://styles/mapbox/dark-v9",
+                "map_renderer": "mapbox",
+                "other_param": "value",
+            }
+        ),
+    )
+
+    modified = _downgrade_deckgl_slice(slc)
+
+    assert modified is True
+    params = json.loads(slc.params)
+    assert params["mapbox_style"] == "mapbox://styles/mapbox/dark-v9"
+    assert "map_renderer" not in params
+    assert params["other_param"] == "value"