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
-
-[](https://www.npmjs.com/package/@superset-ui/legacy-plugin-chart-map-box)
-[](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
-
-[](https://img.shields.io/npm/v/@superset-ui/legacy-preset-chart-deckgl.svg?style=flat-square)
-[](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"