Skip to content

Commit

Permalink
[Feat] Visual channel refactor generalize get accessor and updateTrig…
Browse files Browse the repository at this point in the history
…ger (#1338)

* Generalize getAttributeAccessors and getVisualChannelUpdateTriggers function across layers
* refactor point arc line trip h3 geojson icon s2 layer to use visual channel methods
* fix tests
Signed-off-by: Shan He <heshan0131@gmail.com>
  • Loading branch information
heshan0131 committed Dec 3, 2020
1 parent c1d4943 commit 524fc59
Show file tree
Hide file tree
Showing 52 changed files with 991 additions and 1,062 deletions.
20 changes: 19 additions & 1 deletion examples/demo-app/src/data/sample-trip-data.js
Expand Up @@ -4002,7 +4002,25 @@ export const sampleTripDataConfig = {
},
color: [255, 0, 0],
label: 'pickup',
isVisible: true
isVisible: true,
visConfig: {
colorRange: {
colorMap: [
['apple tree', '#FF000'],
['banana peel', '#00FF00'],
['banana peel 2', '#0000FF'],
['mango mint pineapple juice', '#555555'],
['orange peel', '#111111'],
['orange peel 0', '#222222']
]
}
}
},
visualChannels: {
colorField: {
name: 'fare_type',
type: 'string'
}
}
},
{
Expand Down
8 changes: 2 additions & 6 deletions src/components/common/action-panel.js
Expand Up @@ -36,9 +36,7 @@ const StyledItem = styled.div`
background-color: ${props => props.theme.dropdownListBgd};
width: ${props => props.theme.actionPanelWidth}px;
position: relative;
${props => (props.color ? `border-left: 3px solid rgb(${props.color});` : '')}
:hover {
${props => (props.color ? `border-left: 3px solid rgb(${props.color});` : '')} :hover {
cursor: pointer;
color: ${props => props.theme.textColorHl};
.nested-group {
Expand Down Expand Up @@ -150,9 +148,7 @@ const StyledActionPanel = styled.div`
${props =>
props.direction === 'column'
? `border-bottom: 1px solid ${props.theme.panelHeaderIcon}`
: `border-right: 1px solid ${props.theme.panelHeaderIcon}`}
&:last-of-type {
: `border-right: 1px solid ${props.theme.panelHeaderIcon}`} &:last-of-type {
border-bottom: 0;
}
}
Expand Down
57 changes: 39 additions & 18 deletions src/components/common/color-legend.js
Expand Up @@ -26,6 +26,7 @@ import {format} from 'd3-format';
import moment from 'moment';
import {SCALE_TYPES, SCALE_FUNC, ALL_FIELD_TYPES} from 'constants/default-settings';
import {getTimeWidgetHintFormatter} from 'utils/filter-utils';
import {isObject} from 'utils/utils';

const ROW_H = 10;
const GAP = 4;
Expand Down Expand Up @@ -105,7 +106,7 @@ export default class ColorLegend extends Component {
scaleType: PropTypes.string,
domain: PropTypes.oneOfType([PropTypes.array, PropTypes.object]),
fieldType: PropTypes.string,
range: PropTypes.arrayOf(PropTypes.string),
range: PropTypes.object,
labelFormat: PropTypes.func
};

Expand All @@ -122,28 +123,48 @@ export default class ColorLegend extends Component {
this.labelFormatSelector,
this.fieldTypeSelector,
(domain, range, scaleType, labelFormat, fieldType) => {
const scaleFunction = SCALE_FUNC[scaleType];
// color scale can only be quantize, quantile or ordinal
const scale = scaleFunction()
.domain(domain)
.range(range);

if (scaleType === SCALE_TYPES.ordinal) {
return getOrdinalLegends(scale);
const empty = {
data: [],
labels: []
};
if (!range) {
return empty;
}

const formatLabel = labelFormat || getQuantLabelFormat(scale.domain(), fieldType);

return getQuantLegends(scale, formatLabel);
if (isObject(range.colorLegends)) {
return {
data: Object.keys(range.colorLegends),
labels: Object.values(range.colorLegends)
};
} else if (Array.isArray(range.colorMap)) {
return {
data: range.colorMap.map(cm => cm[1]),
labels: range.colorMap.map(cm => cm[0])
};
} else if (Array.isArray(range.colors)) {
if (!domain || !scaleType) {
return empty;
}

const scaleFunction = SCALE_FUNC[scaleType];
// color scale can only be quantize, quantile or ordinal
const scale = scaleFunction()
.domain(domain)
.range(range.colors);

if (scaleType === SCALE_TYPES.ordinal) {
return getOrdinalLegends(scale);
}

const formatLabel = labelFormat || getQuantLabelFormat(scale.domain(), fieldType);

return getQuantLegends(scale, formatLabel);
}
return empty;
}
);

render() {
const {width, scaleType, domain, range, displayLabel = true} = this.props;

if (!domain || !range || !scaleType) {
return null;
}
const {width, displayLabel = true} = this.props;

const legends = this.legendsSelector(this.props);
const height = legends.data.length * (ROW_H + GAP);
Expand Down
4 changes: 2 additions & 2 deletions src/components/common/file-uploader/file-upload.js
Expand Up @@ -51,7 +51,7 @@ const StyledUploadMessage = styled.div`
${media.portable`
font-size: 12px;
`}
`};
`;

export const WarningMsg = styled.span`
Expand Down Expand Up @@ -139,7 +139,7 @@ const StyledDragFileWrapper = styled.div`
`};
${media.portable`
margin-bottom: 16px;
`}
`};
`;

const StyledDisclaimer = styled(StyledMessage)`
Expand Down
2 changes: 2 additions & 0 deletions src/components/common/item-selector/chickleted-input.js
Expand Up @@ -120,6 +120,8 @@ const ChickletedInput = ({
disabled,
key: `${displayOption(item)}_${i}`,
name: displayOption(item),
displayOption,
item,
remove: e => removeItem(item, e)
};
return CustomChickletComponent ? (
Expand Down
1 change: 0 additions & 1 deletion src/components/index.js
Expand Up @@ -180,7 +180,6 @@ export {
// map components
export {default as MapLegend} from 'components/map/map-legend';


export * from './common/styled-components';
import * as Icons from './common/icons';
export {Icons};
Expand Down
45 changes: 8 additions & 37 deletions src/components/map-container.js
Expand Up @@ -38,7 +38,7 @@ import {generateMapboxLayers, updateMapboxLayers} from 'layers/mapbox-utils';
import {OVERLAY_TYPE} from 'layers/base-layer';
import {setLayerBlending} from 'utils/gl-utils';
import {transformRequest} from 'utils/map-style-utils/mapbox-utils';
import {getLayerHoverProp} from 'utils/layer-utils';
import {getLayerHoverProp, renderDeckGlLayer} from 'utils/layer-utils';

// default-settings
import ThreeDBuildingLayer from 'deckgl-layers/3d-building-layer/3d-building-layer';
Expand Down Expand Up @@ -345,41 +345,6 @@ export default function MapContainerFactory(MapPopover, MapControl, Editor) {
return screenCoord && {x: screenCoord[0], y: screenCoord[1]};
}

_renderLayer = (overlays, idx) => {
const {
datasets,
layers,
layerData,
hoverInfo,
clicked,
mapState,
interactionConfig,
animationConfig
} = this.props;
const layer = layers[idx];
const data = layerData[idx];
const {gpuFilter} = datasets[layer.config.dataId] || {};

const objectHovered = clicked || hoverInfo;
const layerCallbacks = {
onSetLayerDomain: val => this._onLayerSetDomain(idx, val)
};

// Layer is Layer class
const layerOverlay = layer.renderLayer({
data,
gpuFilter,
idx,
interactionConfig,
layerCallbacks,
mapState,
animationConfig,
objectHovered
});

return overlays.concat(layerOverlay || []);
};

_renderDeckOverlay(layersToRender) {
const {
mapState,
Expand All @@ -402,7 +367,13 @@ export default function MapContainerFactory(MapPopover, MapControl, Editor) {
.filter(
idx => layers[idx].overlayType === OVERLAY_TYPE.deckgl && layersToRender[layers[idx].id]
)
.reduce(this._renderLayer, []);
.reduce((overlays, idx) => {
const layerCallbacks = {
onSetLayerDomain: val => this._onLayerSetDomain(idx, val)
};
const layerOverlay = renderDeckGlLayer(this.props, layerCallbacks, idx);
return overlays.concat(layerOverlay || []);
}, []);
}

if (mapStyle.visibleLayerGroups['3d building']) {
Expand Down
2 changes: 1 addition & 1 deletion src/components/map/layer-hover-info.js
Expand Up @@ -119,7 +119,7 @@ const EntryInfoRow = ({item, fields, data, primaryData, compareType}) => {
compareType
});

return <Row name={item.name} value={displayValue} deltaValue={displayDeltaValue} />;
return <Row name={field.name} value={displayValue} deltaValue={displayDeltaValue} />;
};

// TODO: supporting comparative value for aggregated cells as well
Expand Down
59 changes: 27 additions & 32 deletions src/components/map/map-legend.js
Expand Up @@ -27,16 +27,18 @@ import {CHANNEL_SCALES, DIMENSIONS} from 'constants/default-settings';
import {FormattedMessage} from 'localization';

export const StyledMapControlLegend = styled.div`
padding: 10px 0 10px ${props => props.theme.mapControl.padding}px;
padding: 10px ${props => props.theme.mapControl.padding}px 10px
${props => props.theme.mapControl.padding}px;
font-size: 11px;
border-bottom-color: ${props => props.theme.panelBorderColor};
border-bottom-style: solid;
border-bottom-width: ${props => (props.last ? 0 : '1px')};
width: ${props => props.width}px;
.legend--layer_name {
font-size: 12px;
padding-right: ${props => props.theme.mapControl.padding}px;
color: ${props => props.theme.textColorHl};
color: ${props => props.theme.textColor};
font-weight: 500;
}
.legend--layer_type {
Expand All @@ -55,7 +57,7 @@ export const StyledMapControlLegend = styled.div`
}
.legend--layer_color_field {
color: ${props => props.theme.textColor};
color: ${props => props.theme.textColorHl};
font-weight: 500;
}
Expand All @@ -67,7 +69,6 @@ export const StyledMapControlLegend = styled.div`
export const VisualChannelMetric = ({name}) => {
return (
<div className="legend--layer__title">
<span className="legend--layer_by">by </span>
<span className="legend--layer_color_field">
<FormattedMessage id={name} />
</span>
Expand All @@ -81,6 +82,7 @@ export const LayerSizeLegend = ({label, name}) => (
<span className="legend--layer_by">
<FormattedMessage id={label} />
</span>
<span className="legend--layer_by"> by </span>
</p>
<VisualChannelMetric name={name} />
</div>
Expand All @@ -97,28 +99,13 @@ export const SingleColorLegend = React.memo(({width, color}) => (
displayLabel={false}
domain={SingleColorDomain}
fieldType={null}
range={[rgb(...color).toString()]}
range={{colors: [rgb(...color).toString()]}}
width={width}
/>
));

SingleColorLegend.displayName = 'SingleColorLegend';

export const MultiColorLegend = React.memo(
({colorRange, colorScale, colorDomain, colorField, width}) => (
<ColorLegend
scaleType={colorScale}
displayLabel
domain={colorDomain}
fieldType={(colorField && colorField.type) || 'real'}
range={colorRange.colors}
width={width}
/>
)
);

MultiColorLegend.displayName = 'MultiColorLegend';

export const LayerColorLegend = React.memo(({description, config, width, colorChannel}) => {
const enableColorBy = description.measure;
const {scale, field, domain, range, property} = colorChannel;
Expand All @@ -132,11 +119,12 @@ export const LayerColorLegend = React.memo(({description, config, width, colorCh
{enableColorBy ? <VisualChannelMetric name={enableColorBy} /> : null}
<div className="legend--layer_color-legend">
{enableColorBy ? (
<MultiColorLegend
colorScale={colorScale}
colorField={colorField}
colorDomain={colorDomain}
colorRange={colorRange}
<ColorLegend
scaleType={colorScale}
displayLabel
domain={colorDomain}
fieldType={(colorField && colorField.type) || 'real'}
range={colorRange}
width={width}
/>
) : (
Expand All @@ -157,15 +145,19 @@ LayerColorLegend.displayName = 'LayerColorLegend';
const isColorChannel = visualChannel =>
[CHANNEL_SCALES.color, CHANNEL_SCALES.colorAggr].includes(visualChannel.channelScaleType);

const MAP_LEGEND_WIDTH = DIMENSIONS.mapControl.width - 2 * DIMENSIONS.mapControl.padding;

const MapLegend = ({layers = []}) => (
/**
* @param {Layer[]} layers
* @param {number} width
* @param {object} options
* @param {boolean} options.showLayerName
*/
const MapLegend = ({layers = [], width, options = {}}) => (
<div className="map-legend">
{layers.map((layer, index) => {
if (!layer.isValidToSave()) {
if (!layer.isValidToSave() || layer.config.hidden) {
return null;
}

const containerW = width || DIMENSIONS.mapControl.width;
const colorChannels = Object.values(layer.visualChannels).filter(isColorChannel);
const nonColorChannels = Object.values(layer.visualChannels).filter(
vc => !isColorChannel(vc)
Expand All @@ -176,15 +168,18 @@ const MapLegend = ({layers = []}) => (
className="legend--layer"
last={index === layers.length - 1}
key={index}
width={containerW}
>
<div className="legend--layer_name">{layer.config.label}</div>
{options.showLayerName !== false ? (
<div className="legend--layer_name">{layer.config.label}</div>
) : null}
{colorChannels.map(colorChannel =>
!colorChannel.condition || colorChannel.condition(layer.config) ? (
<LayerColorLegend
key={colorChannel.key}
description={layer.getVisualChannelDescription(colorChannel.key)}
config={layer.config}
width={MAP_LEGEND_WIDTH}
width={containerW - 2 * DIMENSIONS.mapControl.padding}
colorChannel={colorChannel}
/>
) : null
Expand Down

0 comments on commit 524fc59

Please sign in to comment.