diff --git a/superset-frontend/package-lock.json b/superset-frontend/package-lock.json index 7dae691f2ca5..2339a4ab8ea8 100644 --- a/superset-frontend/package-lock.json +++ b/superset-frontend/package-lock.json @@ -23180,7 +23180,8 @@ "version": "8.8.2", "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.8.2.tgz", "integrity": "sha512-x9VuX+R/jcFj1DHo/fCp99esgGDWiHENrKxaCENuCxpoMCmAt/COCGVDwA7kleEpEzJjDnvh3yGoOuLu0Dtllw==", - "devOptional": true, + "optional": true, + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.1", "json-schema-traverse": "^1.0.0", @@ -23196,7 +23197,8 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "devOptional": true + "optional": true, + "peer": true }, "node_modules/ajv-keywords": { "version": "3.5.2", @@ -23764,6 +23766,7 @@ "version": "1.2.5", "resolved": "https://registry.npmjs.org/aphrodite/-/aphrodite-1.2.5.tgz", "integrity": "sha1-g1jDbIC7A67puXFlqqcBhiJbSYM=", + "peer": true, "dependencies": { "asap": "^2.0.3", "inline-style-prefixer": "^3.0.1", @@ -25363,7 +25366,8 @@ "node_modules/bowser": { "version": "1.9.4", "resolved": "https://registry.npmjs.org/bowser/-/bowser-1.9.4.tgz", - "integrity": "sha512-9IdMmj2KjigRq6oWhmwv1W36pDuA4STQZ8q6YO9um+x07xgYNCD3Oou+WP/3L1HNz7iqythGet3/p4wvc8AAwQ==" + "integrity": "sha512-9IdMmj2KjigRq6oWhmwv1W36pDuA4STQZ8q6YO9um+x07xgYNCD3Oou+WP/3L1HNz7iqythGet3/p4wvc8AAwQ==", + "peer": true }, "node_modules/boxen": { "version": "5.1.2", @@ -28259,6 +28263,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/css-in-js-utils/-/css-in-js-utils-2.0.1.tgz", "integrity": "sha512-PJF0SpJT+WdbVVt0AOYp9C8GnuruRlL/UFW7932nLWmFLQTaWEzTBQEx7/hn4BuV+WON75iAViSUJLiU3PKbpA==", + "peer": true, "dependencies": { "hyphenate-style-name": "^1.0.2", "isobject": "^3.0.1" @@ -36631,7 +36636,8 @@ "node_modules/hyphenate-style-name": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/hyphenate-style-name/-/hyphenate-style-name-1.0.4.tgz", - "integrity": "sha512-ygGZLjmXfPHj+ZWh6LwbC37l43MhfztxetbFCoYTM2VjkIUpeHgSNn7QIyVFj7YQ1Wl9Cbw5sholVJPzWvC2MQ==" + "integrity": "sha512-ygGZLjmXfPHj+ZWh6LwbC37l43MhfztxetbFCoYTM2VjkIUpeHgSNn7QIyVFj7YQ1Wl9Cbw5sholVJPzWvC2MQ==", + "peer": true }, "node_modules/iconv-lite": { "version": "0.4.24", @@ -37065,6 +37071,7 @@ "version": "3.0.8", "resolved": "https://registry.npmjs.org/inline-style-prefixer/-/inline-style-prefixer-3.0.8.tgz", "integrity": "sha1-hVG45bTVcyROZqNLBPfTIHaitTQ=", + "peer": true, "dependencies": { "bowser": "^1.7.3", "css-in-js-utils": "^2.0.0" @@ -54252,7 +54259,8 @@ "node_modules/string-hash": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/string-hash/-/string-hash-1.1.3.tgz", - "integrity": "sha1-6Kr8CsGFW0Zmkp7X3RJ1311sgRs=" + "integrity": "sha1-6Kr8CsGFW0Zmkp7X3RJ1311sgRs=", + "peer": true }, "node_modules/string-length": { "version": "4.0.1", @@ -64788,7 +64796,6 @@ "@vx/scale": "0.0.140", "@vx/shape": "0.0.140", "@vx/tooltip": "0.0.140", - "aphrodite": "^1.2.0", "d3-array": "^1.2.0", "d3-format": "^1.2.0", "d3-selection": "^1.1.0", @@ -80581,15 +80588,13 @@ "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", "devOptional": true, - "requires": { - "ajv": "^8.0.0" - }, + "requires": {}, "dependencies": { "ajv": { - "version": "8.8.2", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.8.2.tgz", + "version": "https://registry.npmjs.org/ajv/-/ajv-8.8.2.tgz", "integrity": "sha512-x9VuX+R/jcFj1DHo/fCp99esgGDWiHENrKxaCENuCxpoMCmAt/COCGVDwA7kleEpEzJjDnvh3yGoOuLu0Dtllw==", - "devOptional": true, + "optional": true, + "peer": true, "requires": { "fast-deep-equal": "^3.1.1", "json-schema-traverse": "^1.0.0", @@ -80601,7 +80606,8 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "devOptional": true + "optional": true, + "peer": true } } }, @@ -81032,6 +81038,7 @@ "version": "1.2.5", "resolved": "https://registry.npmjs.org/aphrodite/-/aphrodite-1.2.5.tgz", "integrity": "sha1-g1jDbIC7A67puXFlqqcBhiJbSYM=", + "peer": true, "requires": { "asap": "^2.0.3", "inline-style-prefixer": "^3.0.1", @@ -82267,7 +82274,8 @@ "bowser": { "version": "1.9.4", "resolved": "https://registry.npmjs.org/bowser/-/bowser-1.9.4.tgz", - "integrity": "sha512-9IdMmj2KjigRq6oWhmwv1W36pDuA4STQZ8q6YO9um+x07xgYNCD3Oou+WP/3L1HNz7iqythGet3/p4wvc8AAwQ==" + "integrity": "sha512-9IdMmj2KjigRq6oWhmwv1W36pDuA4STQZ8q6YO9um+x07xgYNCD3Oou+WP/3L1HNz7iqythGet3/p4wvc8AAwQ==", + "peer": true }, "boxen": { "version": "5.1.2", @@ -84567,6 +84575,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/css-in-js-utils/-/css-in-js-utils-2.0.1.tgz", "integrity": "sha512-PJF0SpJT+WdbVVt0AOYp9C8GnuruRlL/UFW7932nLWmFLQTaWEzTBQEx7/hn4BuV+WON75iAViSUJLiU3PKbpA==", + "peer": true, "requires": { "hyphenate-style-name": "^1.0.2", "isobject": "^3.0.1" @@ -90958,7 +90967,8 @@ "hyphenate-style-name": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/hyphenate-style-name/-/hyphenate-style-name-1.0.4.tgz", - "integrity": "sha512-ygGZLjmXfPHj+ZWh6LwbC37l43MhfztxetbFCoYTM2VjkIUpeHgSNn7QIyVFj7YQ1Wl9Cbw5sholVJPzWvC2MQ==" + "integrity": "sha512-ygGZLjmXfPHj+ZWh6LwbC37l43MhfztxetbFCoYTM2VjkIUpeHgSNn7QIyVFj7YQ1Wl9Cbw5sholVJPzWvC2MQ==", + "peer": true }, "iconv-lite": { "version": "0.4.24", @@ -91276,6 +91286,7 @@ "version": "3.0.8", "resolved": "https://registry.npmjs.org/inline-style-prefixer/-/inline-style-prefixer-3.0.8.tgz", "integrity": "sha1-hVG45bTVcyROZqNLBPfTIHaitTQ=", + "peer": true, "requires": { "bowser": "^1.7.3", "css-in-js-utils": "^2.0.0" @@ -101671,8 +101682,7 @@ "integrity": "sha512-JZUw7hBsAHXK7PTyErJyI7SopSBFRcFHDjWW5SWjcugY0i6iH7f+eJkY8cJmGMlZ1C9xz1J3Vjz0plFpavVeRg==", "requires": { "@babel/runtime": "^7.2.0", - "invariant": "^2.2.4", - "prop-types": "^15.5.7" + "invariant": "^2.2.4" } }, "react-split": { @@ -104476,7 +104486,8 @@ "string-hash": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/string-hash/-/string-hash-1.1.3.tgz", - "integrity": "sha1-6Kr8CsGFW0Zmkp7X3RJ1311sgRs=" + "integrity": "sha1-6Kr8CsGFW0Zmkp7X3RJ1311sgRs=", + "peer": true }, "string-length": { "version": "4.0.1", @@ -108266,8 +108277,6 @@ "is-scoped": "^2.1.0", "lodash": "^4.17.10", "log-symbols": "^4.0.0", - "mem-fs": "^1.2.0 || ^2.0.0", - "mem-fs-editor": "^8.1.2 || ^9.0.0", "minimatch": "^3.0.4", "npmlog": "^5.0.1", "p-queue": "^6.6.2", diff --git a/superset-frontend/packages/superset-ui-core/src/color/colorSchemes/sequential/common.ts b/superset-frontend/packages/superset-ui-core/src/color/colorSchemes/sequential/common.ts index 73e84c257f93..18528ca5a64f 100644 --- a/superset-frontend/packages/superset-ui-core/src/color/colorSchemes/sequential/common.ts +++ b/superset-frontend/packages/superset-ui-core/src/color/colorSchemes/sequential/common.ts @@ -218,6 +218,11 @@ const schemes = [ isDiverging: false, colors: ['#f6EFA6', '#D88273', '#BF444C'], }, + { + id: 'deck_gl_heatmap_gradient', + label: 'Deck.gl Heatmap Default', + colors: ['#bd0026', '#f03b20', '#fd8d3c', '#feb24c', '#fed976', '#ffffb2'], + }, ].map(s => new SequentialScheme(s)); export default schemes; diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/factory.tsx b/superset-frontend/plugins/legacy-preset-chart-deckgl/src/factory.tsx index 8358dd066029..248c2826ad16 100644 --- a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/factory.tsx +++ b/superset-frontend/plugins/legacy-preset-chart-deckgl/src/factory.tsx @@ -38,7 +38,7 @@ type deckGLComponentProps = { viewport: Viewport; width: number; }; -interface getLayerType { +export interface getLayerType { ( formData: QueryFormData, payload: JsonObject, diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Heatmap/Heatmap.tsx b/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Heatmap/Heatmap.tsx new file mode 100644 index 000000000000..2bd1f63ce799 --- /dev/null +++ b/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Heatmap/Heatmap.tsx @@ -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 { HeatmapLayer } from 'deck.gl'; +import React from 'react'; +import { t, getSequentialSchemeRegistry } from '@superset-ui/core'; +import { commonLayerProps } from '../common'; +import sandboxedEval from '../../utils/sandbox'; +import { hexToRGB } from '../../utils/colors'; +import { createDeckGLComponent, getLayerType } from '../../factory'; +import TooltipRow from '../../TooltipRow'; + +function setTooltipContent(o: any) { + return ( +
+ +
+ ); +} +export const getLayer: getLayerType = ( + formData, + payload, + onAddFilter, + setTooltip, +) => { + const fd = formData; + const { + intensity = 1, + radius_pixels: radiusPixels = 30, + aggregation = 'SUM', + js_data_mutator: jsFnMutator, + linear_color_scheme: colorScheme, + } = fd; + let data = payload.data.features; + + if (jsFnMutator) { + // Applying user defined data mutator if defined + const jsFnMutatorFunction = sandboxedEval(fd.js_data_mutator); + data = jsFnMutatorFunction(data); + } + + const colorScale = getSequentialSchemeRegistry() + ?.get(colorScheme) + ?.createLinearScale([0, 6]); + const colorRange = colorScale + ?.range() + ?.map(color => hexToRGB(color)) + ?.reverse(); + + return new HeatmapLayer({ + id: `heatmp-layer-${fd.slice_id}`, + data, + intensity, + radiusPixels, + colorRange, + aggregation: aggregation.toUpperCase(), + getPosition: (d: { position: number[]; weight: number }) => d.position, + getWeight: (d: { position: number[]; weight: number }) => + d.weight ? d.weight : 1, + ...commonLayerProps(fd, setTooltip, setTooltipContent), + }); +}; + +function getPoints(data: any[]) { + return data.map(d => d.position); +} + +export default createDeckGLComponent(getLayer, getPoints); diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Heatmap/controlPanel.ts b/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Heatmap/controlPanel.ts new file mode 100644 index 000000000000..6fa41c2e21ab --- /dev/null +++ b/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Heatmap/controlPanel.ts @@ -0,0 +1,148 @@ +/** + * 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 { + ControlPanelConfig, + sections, + formatSelectOptions, +} from '@superset-ui/chart-controls'; +import { + t, + validateNonEmpty, + legacyValidateNumber, + legacyValidateInteger, +} from '@superset-ui/core'; +import { + autozoom, + filterNulls, + jsColumns, + jsDataMutator, + jsOnclickHref, + jsTooltip, + mapboxStyle, + spatial, + viewport, +} from '../../utilities/Shared_DeckGL'; + +const INTENSITY_OPTIONS = Array.from( + { length: 10 }, + (_, index) => (index + 1) / 10, +); +const RADIUS_PIXEL_OPTIONS = Array.from( + { length: 14 }, + (_, index) => index * 5 + 5, +); + +const config: ControlPanelConfig = { + controlPanelSections: [ + sections.legacyRegularTime, + { + label: t('Query'), + expanded: true, + controlSetRows: [ + [spatial], + ['size'], + ['row_limit'], + [filterNulls], + ['adhoc_filters'], + [ + { + name: 'intensity', + config: { + type: 'SelectControl', + label: t('Intesity'), + description: t( + 'Intensity is the value multiplied by the weight to obtain the final weight', + ), + freeForm: true, + clearable: false, + validators: [legacyValidateNumber], + default: 1, + choices: formatSelectOptions(INTENSITY_OPTIONS), + }, + }, + ], + [ + { + name: 'radius_pixels', + config: { + type: 'SelectControl', + label: t('Intensity Radius'), + description: t( + 'Intensity Radius is the radius at which the weight is distributed', + ), + freeForm: true, + clearable: false, + validators: [legacyValidateInteger], + default: 30, + choices: formatSelectOptions(RADIUS_PIXEL_OPTIONS), + }, + }, + ], + ], + }, + { + label: t('Map'), + controlSetRows: [ + [mapboxStyle, viewport], + ['linear_color_scheme'], + [autozoom], + [ + { + name: 'aggregation', + config: { + type: 'SelectControl', + label: t('Aggregation'), + description: t( + 'The function to use when aggregating points into groups', + ), + default: 'sum', + clearable: false, + renderTrigger: true, + choices: [ + ['sum', t('sum')], + ['mean', t('mean')], + ], + }, + }, + ], + ], + }, + { + label: t('Advanced'), + controlSetRows: [ + [jsColumns], + [jsDataMutator], + [jsTooltip], + [jsOnclickHref], + ], + }, + ], + controlOverrides: { + size: { + label: t('Weight'), + description: t("Metric used as a weight for the grid's coloring"), + validators: [validateNonEmpty], + }, + }, + formDataOverrides: formData => ({ + ...formData, + }), +}; + +export default config; diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Heatmap/images/thumbnail.png b/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Heatmap/images/thumbnail.png new file mode 100644 index 000000000000..e120e6cb03a0 Binary files /dev/null and b/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Heatmap/images/thumbnail.png differ diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Heatmap/images/thumbnailLarge.png b/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Heatmap/images/thumbnailLarge.png new file mode 100644 index 000000000000..e120e6cb03a0 Binary files /dev/null and b/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Heatmap/images/thumbnailLarge.png differ diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Heatmap/index.ts b/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Heatmap/index.ts new file mode 100644 index 000000000000..00d1c99af06b --- /dev/null +++ b/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Heatmap/index.ts @@ -0,0 +1,45 @@ +/** + * 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 { t, ChartMetadata, ChartPlugin } from '@superset-ui/core'; +import transformProps from '../../transformProps'; +import controlPanel from './controlPanel'; +import thumbnail from './images/thumbnail.png'; + +const metadata = new ChartMetadata({ + category: t('Map'), + credits: ['https://uber.github.io/deck.gl'], + description: t( + 'Uses Gaussian Kernel Density Estimation to visualize spatial distribution of data', + ), + name: t('deck.gl Heatmap'), + thumbnail, + useLegacyApi: true, + tags: [t('deckGL'), t('Spatial'), t('Comparison'), t('Experimental')], +}); + +export default class HeatmapChartPlugin extends ChartPlugin { + constructor() { + super({ + loadChart: () => import('./Heatmap'), + controlPanel, + metadata, + transformProps, + }); + } +} diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/preset.js b/superset-frontend/plugins/legacy-preset-chart-deckgl/src/preset.js index 9360bb44d38e..2f9a1c6b9e1f 100644 --- a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/preset.js +++ b/superset-frontend/plugins/legacy-preset-chart-deckgl/src/preset.js @@ -21,6 +21,7 @@ import ArcChartPlugin from './layers/Arc'; import GeoJsonChartPlugin from './layers/Geojson'; import GridChartPlugin from './layers/Grid'; import HexChartPlugin from './layers/Hex'; +import HeatmapChartPlugin from './layers/Heatmap'; import MultiChartPlugin from './Multi'; import PathChartPlugin from './layers/Path'; import PolygonChartPlugin from './layers/Polygon'; @@ -36,6 +37,7 @@ export default class DeckGLChartPreset extends Preset { new GeoJsonChartPlugin().configure({ key: 'deck_geojson' }), new GridChartPlugin().configure({ key: 'deck_grid' }), new HexChartPlugin().configure({ key: 'deck_hex' }), + new HeatmapChartPlugin().configure({ key: 'deck_heatmap' }), new MultiChartPlugin().configure({ key: 'deck_multi' }), new PathChartPlugin().configure({ key: 'deck_path' }), new PolygonChartPlugin().configure({ key: 'deck_polygon' }), 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 index 5b600d05f8e5..02c511cbc9b3 100644 --- a/superset-frontend/plugins/legacy-preset-chart-deckgl/types/external.d.ts +++ b/superset-frontend/plugins/legacy-preset-chart-deckgl/types/external.d.ts @@ -17,3 +17,31 @@ * under the License. */ declare module '@math.gl/web-mercator'; + +declare module 'deck.gl' { + import { Layer, LayerProps } from '@deck.gl/core'; + + interface HeatmapLayerProps extends LayerProps { + 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; + } + + export class HeatmapLayer extends Layer< + T, + HeatmapLayerProps + > { + constructor(props: HeatmapLayerProps); + } +} + +declare module '*.png' { + const value: any; + export default value; +} diff --git a/superset/viz.py b/superset/viz.py index 8abb6038e8b5..a7b4a8952abe 100644 --- a/superset/viz.py +++ b/superset/viz.py @@ -2988,6 +2988,27 @@ def get_data(self, df: pd.DataFrame) -> VizData: return super().get_data(df) +class DeckHeatmap(BaseDeckGLViz): + + """deck.gl's HeatmapLayer""" + + viz_type = "deck_heatmap" + verbose_name = _("Deck.gl - Heatmap") + spatial_control_keys = ["spatial"] + + def get_properties(self, data: Dict[str, Any]) -> Dict[str, Any]: + return { + "position": data.get("spatial"), + "weight": (data.get(self.metric_label) if self.metric_label else None) or 1, + } + + def get_data(self, df: pd.DataFrame) -> VizData: + self.metric_label = ( # pylint: disable=attribute-defined-outside-init + utils.get_metric_name(self.metric) if self.metric else None + ) + return super().get_data(df) + + class DeckGeoJson(BaseDeckGLViz): """deck.gl's GeoJSONLayer"""