UNPKG

kepler.gl.geoiq

Version:

kepler.gl is a webgl based application to visualize large scale location data in the browser

451 lines (416 loc) 13.3 kB
// Copyright (c) 2019 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. import React, {Component} from 'react'; import PropTypes from 'prop-types'; import styled from 'styled-components'; import { Button, PanelLabel, SidePanelSection } from 'components/common/styled-components'; import ItemSelector from 'components/common/item-selector/item-selector'; import VisConfigByFieldSelector from './vis-config-by-field-selector'; import LayerTypeSelector from './layer-type-selector'; import DimensionScaleSelector from './dimension-scale-selector'; import ColorSelector from './color-selector'; import SourceDataSelector from 'components/side-panel/source-data-selector'; import LayerConfigGroup, { ConfigGroupCollapsibleContent } from './layer-config-group'; import WidgetLayerSelector from './widget-layer-selector'; import WidgetAggregationSelector from './widget-aggregation-selector'; import WidgetFieldSelector from './widget-field-selector'; import WidgetConfigGroup from './widget-config-group'; import {capitalizeFirstLetter} from 'utils/utils'; import {CHANNEL_SCALE_SUPPORTED_FIELDS} from 'constants/default-settings'; // import files from '../../../../dist/components/common/icons/files'; // import layerPanel from '../../../../dist/components/side-panel/layer-panel/layer-panel'; const StyledLayerConfigurator = styled.div.attrs({ className: 'layer-panel__config' })` position: relative; margin-top: 12px; `; const StyledWidgetVisualConfigurator = styled.div.attrs({ className: 'layer-panel__config__visualC-config' })` margin-top: 12px; `; export default class WidgetConfigurator extends Component { static propTypes = { widget: PropTypes.object.isRequired, datasets: PropTypes.object.isRequired, widgetTypeOptions: PropTypes.arrayOf(PropTypes.any).isRequired, openModal: PropTypes.func.isRequired, updateLayerConfig: PropTypes.func.isRequired, updateLayerType: PropTypes.func.isRequired, updateLayerVisConfig: PropTypes.func.isRequired, updateLayerVisualChannelConfig: PropTypes.func.isRequired }; _renderFunctionWidgetConfig(props) { return this._renderAggregationWidgetConfig(props); } _renderAggregationWidgetConfig({ layers, widget, datasets, dataId, widgetConfiguratorProps }) { const {fields} = widget.config.dataId ? datasets[widget.config.dataId] : {}; return ( <StyledWidgetVisualConfigurator> <LayerConfigGroup label={'layer'}> <WidgetLayerSelector widget={widget} layers={layers} dataId={dataId} onSelect={value => this.props.updateLayerConfig({selectedLayer: value}) } /> </LayerConfigGroup> <LayerConfigGroup label={'Aggregation'}> <WidgetAggregationSelector widget={widget} {...widgetConfiguratorProps} /> {widget.config.aggregationType ? ( widget.config.aggregationType === 'count' ? null : ( <WidgetFieldSelector fields={fields} widget={widget} placeholder={'empty'} onChange={this.props.updateLayerConfig} /> ) ) : null} </LayerConfigGroup> <WidgetConfigGroup label={'Dynamic'} property={'bounds'} description={'Calculation on the basis of bounds of map on screen'} {...widgetConfiguratorProps} /> </StyledWidgetVisualConfigurator> ); } _renderCategoryWidgetConfig(props) { return this._renderCategoryConfig(props); } _renderCategoryConfig({ layers, widget, datasets, dataId, widgetConfiguratorProps }) { const {fields} = widget.config.dataId ? datasets[widget.config.dataId] : {}; return ( <StyledWidgetVisualConfigurator> <LayerConfigGroup label={'layer'}> <WidgetLayerSelector widget={widget} layers={layers} dataId={dataId} onSelect={value => this.props.updateLayerConfig({selectedLayer: value}) } /> </LayerConfigGroup> <LayerConfigGroup label={'Categorize by'}> <WidgetFieldSelector fields={fields} widget={widget} placeholder={'empty'} type={'category'} onChange={this.props.updateLayerConfig} /> </LayerConfigGroup> <LayerConfigGroup label={'Aggregation'}> <WidgetAggregationSelector widget={widget} {...widgetConfiguratorProps} /> {widget.config.aggregationType ? ( widget.config.aggregationType === 'count' ? null : ( <WidgetFieldSelector fields={fields} widget={widget} placeholder={'empty'} onChange={this.props.updateLayerConfig} /> ) ) : null} </LayerConfigGroup> <WidgetConfigGroup label={'Dynamic'} property={'bounds'} description={'Calculation on the basis of bounds of map on screen'} {...widgetConfiguratorProps} /> </StyledWidgetVisualConfigurator> ); } _findLayerIdx = (layers, id) => { // console.log('inside_findLayerIdx', layers, id); layers.map((key, lidget) => { if (lidget.id === id) { // console.log(key); } }); }; render() { const { widget, layers, datasets, updateLayerConfig, widgetTypeOptions, updateLayerType } = this.props; const {fields = []} = widget.config.dataId ? datasets[widget.config.dataId] : {}; const {config} = widget; const dataId = config.dataId; const commonConfigProp = { widget, fields }; const visConfiguratorProps = { ...commonConfigProp, onChange: this.props.updateLayerVisConfig }; const widgetConfiguratorProps = { ...commonConfigProp, onChange: updateLayerConfig }; const layerChannelConfigProps = { ...commonConfigProp, onChange: this.props.updateLayerVisualChannelConfig }; const renderTemplate = widget.type && `_render${capitalizeFirstLetter(widget.type)}WidgetConfig`; // console.log('layer inside widget-configurator', layer); return ( <StyledLayerConfigurator> {widget.layerInfoModal ? ( <HowToButton onClick={() => this.props.openModal(widget.layerInfoModal)} /> ) : null} <LayerConfigGroup label={'basic'} collapsible expanded={Object.keys(datasets).length > 1 && widget.type} > <LayerTypeSelector layer={widget} widgetTypeOptions={widgetTypeOptions} onSelect={updateLayerType} /> <ConfigGroupCollapsibleContent> {Object.keys(datasets).length > 1 && ( <SourceDataSelector datasets={datasets} id={widget.id} disabled={widget.tyep && config.selectedField} dataId={config.dataId} onSelect={value => updateLayerConfig({ dataId: value, aggregationType: null, selectedLayer: null, aggregatedData: null }) } /> )} </ConfigGroupCollapsibleContent> </LayerConfigGroup> {this[renderTemplate] && this[renderTemplate]({ widget, layers, datasets, dataId, visConfiguratorProps, layerChannelConfigProps, widgetConfiguratorProps })} </StyledLayerConfigurator> ); } } /* * Componentize config component into pure functional components */ const StyledHowToButton = styled.div` position: absolute; right: 12px; top: -4px; `; export const HowToButton = ({onClick}) => ( <StyledHowToButton> <Button link small onClick={onClick}> How to </Button> </StyledHowToButton> ); export const LayerColorSelector = ({layer, onChange}) => ( <SidePanelSection disabled={layer.config.colorField}> <ColorSelector colorSets={[ { selectedColor: layer.config.color, setColor: rgbValue => onChange({color: rgbValue}) } ]} /> </SidePanelSection> ); export const ArcLayerColorSelector = ({ layer, onChangeConfig, onChangeVisConfig }) => ( <SidePanelSection> <ColorSelector colorSets={[ { selectedColor: layer.config.color, setColor: rgbValue => onChangeConfig({color: rgbValue}), label: 'Source' }, { selectedColor: layer.config.visConfig.targetColor || layer.config.color, setColor: rgbValue => onChangeVisConfig({targetColor: rgbValue}), label: 'Target' } ]} /> </SidePanelSection> ); export const ColorRangeConfig = ({layer, onChange}) => ( <SidePanelSection> <ColorSelector colorSets={[ { selectedColor: layer.config.visConfig.colorRange, isRange: true, setColor: colorRange => onChange({colorRange}) } ]} /> </SidePanelSection> ); export const ChannelByValueSelector = ({ widget, channel, onChange, fields, description }) => { const { channelScaleType, domain, field, key, property, range, scale, defaultMeasure, supportedFieldTypes } = channel; const channelSupportedFieldTypes = supportedFieldTypes || CHANNEL_SCALE_SUPPORTED_FIELDS[channelScaleType]; const supportedFields = fields.filter(({type}) => channelSupportedFieldTypes.includes(type) ); const scaleOptions = widget.getScaleOptions(channel.key); const showScale = !widget.isAggregated && widget.config[scale] && scaleOptions.length > 1; const defaultDescription = `Calculate ${property} based on selected field`; return ( <VisConfigByFieldSelector channel={channel.key} description={description || defaultDescription} domain={widget.config[domain]} fields={supportedFields} id={widget.id} key={`${key}-channel-selector`} property={property} placeholder={defaultMeasure || 'Select a field'} range={widget.config.visConfig[range]} scaleOptions={scaleOptions} scaleType={scale ? widget.config[scale] : null} selectedField={widget.config[field]} showScale={showScale} updateField={val => onChange({[field]: val}, key)} updateScale={val => onChange({[scale]: val}, key)} /> ); }; export const AggrColorScaleSelector = ({layer, onChange}) => { const scaleOptions = layer.getScaleOptions('color'); return Array.isArray(scaleOptions) && scaleOptions.length > 1 ? ( <DimensionScaleSelector label="Color Scale" options={scaleOptions} scaleType={layer.config.colorScale} onSelect={val => onChange({colorScale: val}, 'color')} /> ) : null; }; export const WidgetAggregationSelectors = () => { return <LayerConfigGroup label={'Aggregation'} collapsible />; }; export const AggregationTypeSelector = ({widget, channel, onChange}) => { const {field, aggregation, key} = channel; const selectedField = widget.config[field]; const {visConfig} = widget.config; // aggregation should only be selectable when field is selected const aggregationOptions = widget.getAggregationOptions(key); return ( <SidePanelSection> <PanelLabel>{`Aggregate ${selectedField.name} by`}</PanelLabel> <ItemSelector selectedItems={visConfig[aggregation]} options={aggregationOptions} multiSelect={false} searchable={false} onChange={value => onChange( { visConfig: { ...widget.config.visConfig, [aggregation]: value } }, channel.key ) } /> </SidePanelSection> ); }; /* eslint-enable max-params */