Skip to content

Commit 5a00459

Browse files
feat(deckgl): add selected cross-filter indication (apache#34322)
1 parent 53503e3 commit 5a00459

File tree

19 files changed

+618
-169
lines changed

19 files changed

+618
-169
lines changed

superset-frontend/plugins/legacy-preset-chart-deckgl/src/CategoricalDeckGLContainer.tsx

Lines changed: 23 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ export type CategoricalDeckGLContainerProps = {
8989
width: number;
9090
viewport: Viewport;
9191
getLayer: GetLayerType<unknown>;
92+
getHighlightLayer?: GetLayerType<unknown>;
9293
payload: JsonObject;
9394
onAddFilter?: HandlerFunction;
9495
setControlValue: (control: string, value: JsonValue) => void;
@@ -213,6 +214,7 @@ const CategoricalDeckGLContainer = (props: CategoricalDeckGLContainerProps) => {
213214
const getLayers = useCallback(() => {
214215
const {
215216
getLayer,
217+
getHighlightLayer,
216218
payload,
217219
formData: fd,
218220
onAddFilter,
@@ -244,19 +246,27 @@ const CategoricalDeckGLContainer = (props: CategoricalDeckGLContainerProps) => {
244246
data: { ...payload.data, features },
245247
};
246248

247-
return [
248-
getLayer({
249-
formData: fd,
250-
payload: filteredPayload,
251-
onAddFilter,
252-
setTooltip,
253-
datasource: props.datasource,
254-
onContextMenu,
255-
filterState,
256-
setDataMask,
257-
emitCrossFilters,
258-
}) as Layer,
259-
];
249+
const layerProps = {
250+
formData: fd,
251+
payload: filteredPayload,
252+
onAddFilter,
253+
setTooltip,
254+
datasource: props.datasource,
255+
onContextMenu,
256+
filterState,
257+
setDataMask,
258+
emitCrossFilters,
259+
};
260+
261+
const layer = getLayer(layerProps) as Layer;
262+
263+
if (emitCrossFilters && filterState?.value && getHighlightLayer) {
264+
const highlightLayer = getHighlightLayer(layerProps) as Layer;
265+
266+
return [layer, highlightLayer];
267+
}
268+
269+
return [layer];
260270
}, [addColor, categories, props, setTooltip]);
261271

262272
const toggleCategory = useCallback(

superset-frontend/plugins/legacy-preset-chart-deckgl/src/Multi/Multi.tsx

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,11 @@ import { memo, useCallback, useEffect, useRef, useState } from 'react';
2323
import { isEqual } from 'lodash';
2424
import {
2525
AdhocFilter,
26+
ContextMenuFilters,
27+
DataMask,
2628
Datasource,
2729
ensureIsArray,
30+
FilterState,
2831
HandlerFunction,
2932
isDefined,
3033
JsonObject,
@@ -65,7 +68,15 @@ export type DeckMultiProps = {
6568
height: number;
6669
width: number;
6770
datasource: Datasource;
71+
setDataMask?: (dataMask: DataMask) => void;
72+
onContextMenu?: (
73+
clientX: number,
74+
clientY: number,
75+
filters?: ContextMenuFilters,
76+
) => void;
6877
onSelect: () => void;
78+
filterState?: FilterState;
79+
emitCrossFilters?: boolean;
6980
};
7081

7182
const DeckMulti = (props: DeckMultiProps) => {
@@ -175,16 +186,14 @@ const DeckMulti = (props: DeckMultiProps) => {
175186
const createLayerFromData = useCallback(
176187
(subslice: JsonObject, json: JsonObject): Layer =>
177188
// @ts-ignore TODO(hainenber): define proper type for `form_data.viz_type` and call signature for functions in layerGenerators.
178-
layerGenerators[subslice.form_data.viz_type](
179-
subslice.form_data,
180-
json,
181-
props.onAddFilter,
189+
layerGenerators[subslice.form_data.viz_type]({
190+
formData: subslice.form_data,
191+
payload: json,
182192
setTooltip,
183-
props.datasource,
184-
[],
185-
props.onSelect,
186-
),
187-
[props.onAddFilter, props.onSelect, props.datasource, setTooltip],
193+
datasource: props.datasource,
194+
onSelect: props.onSelect,
195+
}),
196+
[props.onSelect, props.datasource, setTooltip],
188197
);
189198

190199
const loadSingleLayer = useCallback(

superset-frontend/plugins/legacy-preset-chart-deckgl/src/factory.tsx

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ interface GetPointsType {
8787
export function createDeckGLComponent(
8888
getLayer: GetLayerType<unknown>,
8989
getPoints: GetPointsType,
90+
getHighlightLayer?: GetLayerType<unknown>,
9091
) {
9192
// Higher order component
9293
return memo((props: DeckGLComponentProps) => {
@@ -118,7 +119,7 @@ export function createDeckGLComponent(
118119
}
119120
}, []);
120121

121-
const computeLayer = useCallback(
122+
const computeLayers = useCallback(
122123
(props: DeckGLComponentProps) => {
123124
const {
124125
formData,
@@ -130,7 +131,7 @@ export function createDeckGLComponent(
130131
emitCrossFilters,
131132
} = props;
132133

133-
return getLayer({
134+
const layerProps = {
134135
formData,
135136
payload,
136137
onAddFilter,
@@ -139,7 +140,17 @@ export function createDeckGLComponent(
139140
onContextMenu,
140141
filterState,
141142
emitCrossFilters,
142-
}) as Layer;
143+
};
144+
145+
const layer = getLayer(layerProps) as Layer;
146+
147+
if (emitCrossFilters && filterState?.value && getHighlightLayer) {
148+
const highlightLayer = getHighlightLayer(layerProps) as Layer;
149+
150+
return [layer, highlightLayer];
151+
}
152+
153+
return [layer];
143154
},
144155
[setTooltip],
145156
);
@@ -152,7 +163,7 @@ export function createDeckGLComponent(
152163
setCategories(categories);
153164
}, [props]);
154165

155-
const [layer, setLayer] = useState(computeLayer(props));
166+
const [layers, setLayers] = useState(computeLayers(props));
156167

157168
useEffect(() => {
158169
// Only recompute the layer if anything BUT the viewport has changed
@@ -167,9 +178,9 @@ export function createDeckGLComponent(
167178
viewport: null,
168179
};
169180
if (!isEqual(prevFdNoVP, currFdNoVP) || prevPayload !== props.payload) {
170-
setLayer(computeLayer(props));
181+
setLayers(computeLayers(props));
171182
}
172-
}, [computeLayer, prevFormData, prevFilterState, prevPayload, props]);
183+
}, [computeLayers, prevFormData, prevFilterState, prevPayload, props]);
173184

174185
const { formData, payload, setControlValue, height, width } = props;
175186

@@ -179,7 +190,7 @@ export function createDeckGLComponent(
179190
ref={containerRef}
180191
mapboxApiAccessToken={payload.data.mapboxApiKey}
181192
viewport={viewport}
182-
layers={[layer]}
193+
layers={layers}
183194
mapStyle={formData.mapbox_style}
184195
setControlValue={setControlValue}
185196
width={width}
@@ -200,6 +211,7 @@ export function createDeckGLComponent(
200211
export function createCategoricalDeckGLComponent(
201212
getLayer: GetLayerType<Layer>,
202213
getPoints: GetPointsType,
214+
getHighlightLayer?: GetLayerType<Layer>,
203215
) {
204216
return function Component(props: DeckGLComponentProps) {
205217
const {
@@ -224,6 +236,7 @@ export function createCategoricalDeckGLComponent(
224236
setControlValue={setControlValue}
225237
viewport={viewport}
226238
getLayer={getLayer}
239+
getHighlightLayer={getHighlightLayer}
227240
payload={payload}
228241
getPoints={getPoints}
229242
width={width}

superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Arc/Arc.tsx

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import { commonLayerProps } from '../common';
2323
import { GetLayerType, createCategoricalDeckGLComponent } from '../../factory';
2424
import TooltipRow from '../../TooltipRow';
2525
import { Point } from '../../types';
26+
import { HIGHLIGHT_COLOR_ARRAY, TRANSPARENT_COLOR_ARRAY } from '../../utils';
2627

2728
export function getPoints(data: JsonObject[]) {
2829
const points: Point[] = [];
@@ -73,7 +74,7 @@ export const getLayer: GetLayerType<ArcLayer> = function ({
7374

7475
return new ArcLayer({
7576
data,
76-
getSourceColor: (d: any) => {
77+
getSourceColor: (d: JsonObject) => {
7778
if (colorSchemeType === COLOR_SCHEME_TYPES.fixed_color) {
7879
return [sc.r, sc.g, sc.b, 255 * sc.a];
7980
}
@@ -98,7 +99,50 @@ export const getLayer: GetLayerType<ArcLayer> = function ({
9899
filterState,
99100
emitCrossFilters,
100101
}),
102+
opacity: filterState?.value ? 0.1 : 1,
101103
});
102104
};
103105

104-
export default createCategoricalDeckGLComponent(getLayer, getPoints);
106+
export const getHighlightLayer: GetLayerType<ArcLayer> = function ({
107+
formData,
108+
payload,
109+
filterState,
110+
}) {
111+
const fd = formData;
112+
const data = payload.data.features;
113+
114+
const getColor = (d: {
115+
sourcePosition: [number, number];
116+
targetPosition: [number, number];
117+
}) => {
118+
const sourcePosition = filterState?.value[0];
119+
const targetPosition = filterState?.value[1];
120+
121+
if (
122+
sourcePosition &&
123+
targetPosition &&
124+
d.sourcePosition[0] === sourcePosition[0] &&
125+
d.sourcePosition[1] === sourcePosition[1] &&
126+
d.targetPosition[0] === targetPosition[0] &&
127+
d.targetPosition[1] === targetPosition[1]
128+
) {
129+
return HIGHLIGHT_COLOR_ARRAY;
130+
}
131+
132+
return TRANSPARENT_COLOR_ARRAY;
133+
};
134+
135+
return new ArcLayer({
136+
data,
137+
getSourceColor: getColor,
138+
getTargetColor: getColor,
139+
id: `path-hihglight-layer-${fd.slice_id}` as const,
140+
getWidth: fd.stroke_width ? fd.stroke_width : 3,
141+
});
142+
};
143+
144+
export default createCategoricalDeckGLComponent(
145+
getLayer,
146+
getPoints,
147+
getHighlightLayer,
148+
);

superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Contour/Contour.tsx

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,15 @@
1717
* under the License.
1818
*/
1919
import { ContourLayer } from '@deck.gl/aggregation-layers';
20+
import { PolygonLayer } from '@deck.gl/layers';
2021
import { Position } from '@deck.gl/core';
2122
import { t } from '@superset-ui/core';
2223
import { commonLayerProps } from '../common';
2324
import sandboxedEval from '../../utils/sandbox';
2425
import { GetLayerType, createDeckGLComponent } from '../../factory';
2526
import { ColorType } from '../../types';
2627
import TooltipRow from '../../TooltipRow';
28+
import { HIGHLIGHT_COLOR_ARRAY } from '../../utils';
2729

2830
function setTooltipContent(o: any) {
2931
return (
@@ -112,4 +114,56 @@ export function getPoints(data: any[]) {
112114
return data.map(d => d.position);
113115
}
114116

115-
export default createDeckGLComponent(getLayer, getPoints);
117+
export const getHighlightLayer: GetLayerType<PolygonLayer> = function ({
118+
formData,
119+
filterState,
120+
setDataMask,
121+
onContextMenu,
122+
setTooltip,
123+
emitCrossFilters,
124+
}) {
125+
const fd = formData;
126+
127+
const fromLonLat = filterState?.value[0];
128+
const toLonLat = filterState?.value[1];
129+
130+
const minLon = fromLonLat[0];
131+
const maxLon = toLonLat[0];
132+
const minLat = fromLonLat[1];
133+
const maxLat = toLonLat[1];
134+
135+
const boxPolygon = [
136+
[minLon, minLat],
137+
[maxLon, minLat],
138+
[maxLon, maxLat],
139+
[minLon, maxLat],
140+
[minLon, minLat],
141+
];
142+
143+
return new PolygonLayer({
144+
id: `contour-highlight-layer-${fd.slice_id}`,
145+
data: [{ polygon: boxPolygon }],
146+
getPolygon: (d: any) => d.polygon,
147+
getFillColor: [
148+
HIGHLIGHT_COLOR_ARRAY[0],
149+
HIGHLIGHT_COLOR_ARRAY[1],
150+
HIGHLIGHT_COLOR_ARRAY[2],
151+
100,
152+
],
153+
getLineColor: HIGHLIGHT_COLOR_ARRAY,
154+
getLineWidth: 4,
155+
filled: true,
156+
stroked: true,
157+
...commonLayerProps({
158+
formData: fd,
159+
setTooltip,
160+
setTooltipContent,
161+
onContextMenu,
162+
setDataMask,
163+
filterState,
164+
emitCrossFilters,
165+
}),
166+
});
167+
};
168+
169+
export default createDeckGLComponent(getLayer, getPoints, getHighlightLayer);

superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Geojson/Geojson.tsx

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ import fitViewport, { Viewport } from '../../utils/fitViewport';
4343
import { TooltipProps } from '../../components/Tooltip';
4444
import { Point } from '../../types';
4545
import { GetLayerType } from '../../factory';
46+
import { HIGHLIGHT_COLOR_ARRAY } from '../../utils';
4647

4748
type ProcessedFeature = Feature<Geometry, GeoJsonProperties> & {
4849
properties: JsonObject;
@@ -119,7 +120,21 @@ function setTooltipContent(o: JsonObject) {
119120
);
120121
}
121122

122-
const getFillColor = (feature: JsonObject) => feature?.properties?.fillColor;
123+
const getFillColor = (feature: JsonObject, filterStateValue: unknown[]) => {
124+
if (filterStateValue) {
125+
if (
126+
JSON.stringify(feature.geometry.coordinates) ===
127+
JSON.stringify(filterStateValue?.[0])
128+
) {
129+
return HIGHLIGHT_COLOR_ARRAY;
130+
}
131+
132+
const fillColor = feature?.properties?.fillColor;
133+
fillColor[3] = 125;
134+
return fillColor;
135+
}
136+
return feature?.properties?.fillColor;
137+
};
123138
const getLineColor = (feature: JsonObject) => feature?.properties?.strokeColor;
124139

125140
export const getLayer: GetLayerType<GeoJsonLayer> = function ({
@@ -160,7 +175,8 @@ export const getLayer: GetLayerType<GeoJsonLayer> = function ({
160175
extruded: fd.extruded,
161176
filled: fd.filled,
162177
stroked: fd.stroked,
163-
getFillColor,
178+
getFillColor: (feature: JsonObject) =>
179+
getFillColor(feature, filterState?.value),
164180
getLineColor,
165181
getLineWidth: fd.line_width || 1,
166182
pointRadiusScale: fd.point_radius_scale,
@@ -188,6 +204,7 @@ export type DeckGLGeoJsonProps = {
188204
filterState: FilterState;
189205
onContextMenu: HandlerFunction;
190206
setDataMask: SetDataMaskHook;
207+
emitCrossFilters?: boolean;
191208
};
192209

193210
export function getPoints(data: Point[]) {
@@ -242,6 +259,7 @@ const DeckGLGeoJson = (props: DeckGLGeoJsonProps) => {
242259
onAddFilter,
243260
payload,
244261
formData,
262+
emitCrossFilters: props.emitCrossFilters,
245263
});
246264

247265
return (

0 commit comments

Comments
 (0)