Skip to content

Commit

Permalink
[Feat] Map overlay blending (#2086)
Browse files Browse the repository at this point in the history
Signed-off-by: Ihor Dykhta <dikhta.igor@gmail.com>
Co-authored-by: Ilya Boyandin <iboyandin@foursquare.com>
  • Loading branch information
igorDykhta and ilyabo committed Jan 20, 2023
1 parent f4329fc commit 7d9d54b
Show file tree
Hide file tree
Showing 18 changed files with 158 additions and 8 deletions.
1 change: 1 addition & 0 deletions src/actions/src/action-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ const ActionTypes = {
SET_FILTER_ANIMATION_WINDOW: `${ACTION_PREFIX}SET_FILTER_ANIMATION_WINDOW`,
SHOW_DATASET_TABLE: `${ACTION_PREFIX}SHOW_DATASET_TABLE`,
UPDATE_LAYER_BLENDING: `${ACTION_PREFIX}UPDATE_LAYER_BLENDING`,
UPDATE_OVERLAY_BLENDING: `${ACTION_PREFIX}UPDATE_OVERLAY_BLENDING`,
UPDATE_VIS_DATA: `${ACTION_PREFIX}UPDATE_VIS_DATA`,
RENAME_DATASET: `${ACTION_PREFIX}RENAME_DATASET`,
TOGGLE_FILTER_ANIMATION: `${ACTION_PREFIX}TOGGLE_FILTER_ANIMATION`,
Expand Down
20 changes: 20 additions & 0 deletions src/actions/src/vis-state-actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,26 @@ export function updateLayerBlending(
};
}

export type UpdateOverlayBlendingUpdaterAction = {
mode: 'screen' | 'normal' | 'darken';
};

/**
* Update overlay blending mode
* @memberof visStateActions
* @param mode one of `screen`, `normal` and `darken`
* @returns action
* @public
*/
export function updateOverlayBlending(
mode: 'screen' | 'normal' | 'darken'
): Merge<UpdateOverlayBlendingUpdaterAction, {type: typeof ActionTypes.UPDATE_OVERLAY_BLENDING}> {
return {
type: ActionTypes.UPDATE_OVERLAY_BLENDING,
mode
};
}

export type InteractionConfigChangeUpdaterAction = {
config: ValueOf<InteractionConfig>;
};
Expand Down
1 change: 1 addition & 0 deletions src/components/src/kepler-gl.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,7 @@ export const sidePanelSelector = (props: KeplerGLProps, availableProviders, filt
interactionConfig: props.visState.interactionConfig,
mapInfo: props.visState.mapInfo,
layerBlending: props.visState.layerBlending,
overlayBlending: props.visState.overlayBlending,

width: props.sidePanelWidth ? props.sidePanelWidth : DEFAULT_KEPLER_GL_PROPS.width,
availableProviders,
Expand Down
20 changes: 17 additions & 3 deletions src/components/src/map-container.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@

// libraries
import React, {Component, createRef, useMemo} from 'react';
import {withTheme} from 'styled-components';
import styled, {withTheme} from 'styled-components';
import {StaticMap, MapRef} from 'react-map-gl';
import DeckGL from '@deck.gl/react';
import {createSelector, Selector} from 'reselect';
Expand Down Expand Up @@ -101,6 +101,18 @@ const MAP_STYLE: {[key: string]: React.CSSProperties} = {

const LOCALE_CODES_ARRAY = Object.keys(LOCALE_CODES);

interface StyledMapContainerProps {
mixBlendMode?: string;
}

const StyledMap = styled(StyledMapContainer)<StyledMapContainerProps>(
({mixBlendMode = 'normal'}) => `
.overlays {
mix-blend-mode: ${mixBlendMode};
};
`
);

const MAPBOXGL_STYLE_UPDATE = 'style.load';
const MAPBOXGL_RENDER = 'render';
const nop = () => {};
Expand Down Expand Up @@ -826,14 +838,16 @@ export default function MapContainerFactory(
}

render() {
const {visState} = this.props;
return (
<StyledMapContainer
<StyledMap
ref={this._ref}
style={MAP_STYLE.container}
onContextMenu={event => event.preventDefault()}
mixBlendMode={visState.overlayBlending}
>
{this._renderMap()}
</StyledMapContainer>
</StyledMap>
);
}
}
Expand Down
2 changes: 2 additions & 0 deletions src/components/src/side-panel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ export default function SidePanelFactory(
filters,
layers,
layerBlending,
overlayBlending,
layerClasses,
layerOrder,
interactionConfig,
Expand Down Expand Up @@ -235,6 +236,7 @@ export default function SidePanelFactory(
layerClasses={layerClasses}
layerOrder={layerOrder}
layerBlending={layerBlending}
overlayBlending={overlayBlending}
mapStyle={mapStyle}
mapStyleActions={mapStyleActions}
mapStateActions={mapStateActions}
Expand Down
46 changes: 45 additions & 1 deletion src/components/src/side-panel/layer-manager.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ import AddLayerButtonFactory from './layer-panel/add-layer-button';
import ItemSelector from '../common/item-selector/item-selector';
import {PanelLabel, SidePanelDivider, SidePanelSection} from '../common/styled-components';

import {LAYER_BLENDINGS} from '@kepler.gl/constants';
import {LAYER_BLENDINGS, OVERLAY_BLENDINGS} from '@kepler.gl/constants';
import {Layer, LayerClassesType} from '@kepler.gl/layers';
import {UIStateActions, VisStateActions, ActionHandler} from '@kepler.gl/actions';
import {SidePanelItem} from '../types';
Expand All @@ -46,12 +46,18 @@ type LayerBlendingSelectorProps = {
updateLayerBlending: ActionHandler<typeof VisStateActions.updateLayerBlending>;
} & WrappedComponentProps;

type OverlayBlendingSelectorProps = {
overlayBlending: string;
updateOverlayBlending: ActionHandler<typeof VisStateActions.updateOverlayBlending>;
} & WrappedComponentProps;

type LayerManagerProps = {
datasets: Datasets;
layers: Layer[];
layerOrder: number[];
layerClasses: LayerClassesType;
layerBlending: string;
overlayBlending: string;
uiStateActions: typeof UIStateActions;
visStateActions: typeof VisStateActions;
showAddDataModal: () => void;
Expand Down Expand Up @@ -104,6 +110,39 @@ const LayerBlendingSelector = React.memo(
);
LayerBlendingSelector.displayName = 'LayerBlendingSelector';

const OverlayBlendingSelector = React.memo(
({overlayBlending, updateOverlayBlending, intl}: OverlayBlendingSelectorProps) => {
const labeledOverlayBlendings = Object.keys(OVERLAY_BLENDINGS).reduce(
(acc, current) => ({
...acc,
[intl.formatMessage({id: OVERLAY_BLENDINGS[current].label})]: current
}),
{}
);

const onChange = useCallback(
blending => updateOverlayBlending(labeledOverlayBlendings[blending]),
[updateOverlayBlending, labeledOverlayBlendings]
);

return (
<SidePanelSection>
<PanelLabel>
<FormattedMessage id="overlayBlending.title" />
</PanelLabel>
<ItemSelector
selectedItems={intl.formatMessage({id: OVERLAY_BLENDINGS[overlayBlending].label})}
options={Object.keys(labeledOverlayBlendings)}
multiSelect={false}
searchable={false}
onChange={onChange}
/>
</SidePanelSection>
);
}
);
OverlayBlendingSelector.displayName = 'OverlayBlendingSelector';

LayerManagerFactory.deps = [
LayerListFactory,
DatasetLayerGroupFactory,
Expand Down Expand Up @@ -219,6 +258,11 @@ function LayerManagerFactory(
updateLayerBlending={visStateActions.updateLayerBlending}
intl={intl}
/>
<OverlayBlendingSelector
overlayBlending={this.props.overlayBlending}
updateOverlayBlending={visStateActions.updateOverlayBlending}
intl={intl}
/>
</div>
);
}
Expand Down
1 change: 1 addition & 0 deletions src/components/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export type SidePanelProps = {
filters: Filter[];
interactionConfig: InteractionConfig;
layerBlending: string;
overlayBlending: string;
layers: Layer[];
layerClasses: LayerClassesType;
layerOrder: number[];
Expand Down
15 changes: 15 additions & 0 deletions src/constants/src/default-settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -658,6 +658,21 @@ export const NO_VALUE_COLOR: RGBAColor = [0, 0, 0, 0];

export const DEFAULT_PICKING_RADIUS = 3;

export const OVERLAY_BLENDINGS = {
normal: {
label: 'overlayBlending.normal',
value: 'normal'
},
screen: {
label: 'overlayBlending.screen',
value: 'screen'
},
darken: {
label: 'overlayBlending.darken',
value: 'darken'
}
};

export const LAYER_BLENDINGS = {
additive: {
label: 'layerBlending.additive',
Expand Down
9 changes: 8 additions & 1 deletion src/localization/src/translations/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,8 @@ export default {
layerManager: {
addData: 'Add Data',
addLayer: 'Add Layer',
layerBlending: 'Layer Blending'
layerBlending: 'Layer Blending',
overlayBlending: 'Overlay Blending'
},
mapManager: {
mapStyle: 'Map style',
Expand Down Expand Up @@ -413,6 +414,12 @@ export default {
normal: 'normal',
subtractive: 'subtractive'
},
overlayBlending: {
title: 'Map overlay blending',
screen: 'screen',
normal: 'normal',
darken: 'darken'
},
columns: {
title: 'Columns',
lat: 'lat',
Expand Down
21 changes: 20 additions & 1 deletion src/reducers/src/vis-state-merger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ import {
} from '@kepler.gl/utils';

import {LayerColumns, LayerColumn, Layer} from '@kepler.gl/layers';
import {LAYER_BLENDINGS} from '@kepler.gl/constants';
import {LAYER_BLENDINGS, OVERLAY_BLENDINGS} from '@kepler.gl/constants';
import {
CURRENT_VERSION,
Merger,
Expand Down Expand Up @@ -365,6 +365,24 @@ export function mergeLayerBlending<S extends VisState>(
return state;
}

/**
* Merge overlayBlending with saved
*/
export function mergeOverlayBlending<S extends VisState>(
state: S,
overlayBlending: NonNullable<ParsedConfig['visState']>['overlayBlending'],
fromConfig?: boolean
): S {
if (overlayBlending && OVERLAY_BLENDINGS[overlayBlending]) {
return {
...state,
overlayBlending
};
}

return state;
}

/**
* Merge animation config
*/
Expand Down Expand Up @@ -624,6 +642,7 @@ export const VIS_STATE_MERGERS: VisStateMergers = [
{merge: mergeFilters, prop: 'filters', toMergeProp: 'filterToBeMerged'},
{merge: mergeInteractions, prop: 'interactionConfig', toMergeProp: 'interactionToBeMerged'},
{merge: mergeLayerBlending, prop: 'layerBlending'},
{merge: mergeOverlayBlending, prop: 'overlayBlending'},
{merge: mergeSplitMaps, prop: 'splitMaps', toMergeProp: 'splitMapsToBeMerged'},
{merge: mergeAnimationConfig, prop: 'animationConfig'},
{merge: mergeEditor, prop: 'editor'}
Expand Down
14 changes: 14 additions & 0 deletions src/reducers/src/vis-state-updaters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,7 @@ export const INITIAL_VIS_STATE: VisState = {
interactionToBeMerged: undefined,

layerBlending: 'normal',
overlayBlending: 'normal',
hoverInfo: undefined,
clicked: undefined,
mousePos: {},
Expand Down Expand Up @@ -1316,6 +1317,19 @@ export const updateLayerBlendingUpdater = (
layerBlending: action.mode
});

/**
* update overlay blending mode
* @memberof visStateUpdaters
* @public
*/
export const updateOverlayBlendingUpdater = (
state: VisState,
action: VisStateActions.UpdateOverlayBlendingUpdaterAction
): VisState => ({
...state,
overlayBlending: action.mode
});

/**
* Display dataset table in a modal
* @memberof visStateUpdaters
Expand Down
2 changes: 2 additions & 0 deletions src/reducers/src/vis-state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,8 @@ const actionHandler = {

[ActionTypes.UPDATE_LAYER_BLENDING]: visStateUpdaters.updateLayerBlendingUpdater,

[ActionTypes.UPDATE_OVERLAY_BLENDING]: visStateUpdaters.updateOverlayBlendingUpdater,

[ActionTypes.UPDATE_VIS_DATA]: visStateUpdaters.updateVisDataUpdater,

[ActionTypes.RENAME_DATASET]: visStateUpdaters.renameDatasetUpdater,
Expand Down
5 changes: 4 additions & 1 deletion src/schemas/src/vis-state-schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ export interface VisState {
interactionConfig: InteractionConfig;
interactionToBeMerged: any;
layerBlending: string;
overlayBlending: string;
hoverInfo: any;
clicked: any;
mousePos: any;
Expand Down Expand Up @@ -778,7 +779,8 @@ export const propertiesV0 = {
version: VERSIONS.v0,
properties: interactionPropsV0
}),
layerBlending: null
layerBlending: null,
overlayBlending: null
};

export const propertiesV1 = {
Expand All @@ -795,6 +797,7 @@ export const propertiesV1 = {
properties: interactionPropsV1
}),
layerBlending: null,
overlayBlending: null,
splitMaps: new SplitMapsSchema({
key: 'splitMaps',
version: VERSIONS.v1
Expand Down
2 changes: 2 additions & 0 deletions src/types/schemas.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ export type SavedVisState = {
layers: SavedLayer[];
interactionConfig: SavedInteractionConfig;
layerBlending: string;
overlayBlending: string;
splitMaps: SplitMap[];
animationConfig: SavedAnimationConfig;
editor?: SavedEditor;
Expand All @@ -101,6 +102,7 @@ export type ParsedVisState = {
filters?: ParsedFilter[];
interactionConfig?: Partial<SavedInteractionConfig>;
layerBlending?: string;
overlayBlending?: string;
splitMaps?: SplitMap[];
animationConfig?: Partial<SavedAnimationConfig>;
};
Expand Down
3 changes: 2 additions & 1 deletion test/browser/components/side-panel/layer-manager-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,8 @@ const defaultProps = {
layerPanelListView: 'list',
uiStateActions: UIStateActions,
visStateActions: VisStateActions,
layerBlending: 'normal'
layerBlending: 'normal',
overlayBlending: 'normal'
};

test('Components -> LayerManager -> render -> list view', t => {
Expand Down
1 change: 1 addition & 0 deletions test/browser/components/side-panel/side-panel-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ const defaultProps = {
datasets: InitialState.visState.datasets,
filters: InitialState.visState.filters,
layerBlending: InitialState.visState.layerBlending,
overlayBlending: InitialState.visState.overlayBlending,
layerClasses: InitialState.visState.layerClasses,
layerOrder: InitialState.visState.layerOrder,
interactionConfig: InitialState.visState.interactionConfig,
Expand Down
2 changes: 2 additions & 0 deletions test/node/reducers/vis-state-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -2814,6 +2814,7 @@ test('#visStateReducer -> REMOVE_DATASET w filter and layer', t => {
},
editingDataset: oldState.editingDataset,
layerBlending: oldState.layerBlending,
overlayBlending: oldState.overlayBlending,
hoverInfo: oldState.hoverInfo,
clicked: oldState.clicked,
mousePos: oldState.mousePos,
Expand Down Expand Up @@ -3063,6 +3064,7 @@ test('#visStateReducer -> SPLIT_MAP: REMOVE_DATASET', t => {
splitMaps: [{layers: {'point-0': false}}, {layers: {'point-0': true}}],
editingDataset: oldState.editingDataset,
layerBlending: oldState.layerBlending,
overlayBlending: oldState.overlayBlending,
hoverInfo: oldState.hoverInfo,
clicked: oldState.clicked,
mousePos: oldState.mousePos,
Expand Down

0 comments on commit 7d9d54b

Please sign in to comment.