diff --git a/x-pack/legacy/plugins/maps/common/constants.js b/x-pack/legacy/plugins/maps/common/constants.js index 6e7776d43f4d46c..ab8b853377b3b60 100644 --- a/x-pack/legacy/plugins/maps/common/constants.js +++ b/x-pack/legacy/plugins/maps/common/constants.js @@ -140,3 +140,10 @@ export const LAYER_STYLE_TYPE = { VECTOR: 'VECTOR', HEATMAP: 'HEATMAP', }; + +export const COLOR_MAP_TYPE = { + CATEGORICAL: 'CATEGORICAL', + ORDINAL: 'ORDINAL', +}; + +export const COLOR_PALETTE_MAX_SIZE = 10; diff --git a/x-pack/legacy/plugins/maps/public/layers/fields/es_agg_field.js b/x-pack/legacy/plugins/maps/public/layers/fields/es_agg_field.js index 189aad378503438..65109cb99809fce 100644 --- a/x-pack/legacy/plugins/maps/public/layers/fields/es_agg_field.js +++ b/x-pack/legacy/plugins/maps/public/layers/fields/es_agg_field.js @@ -82,7 +82,7 @@ export class ESAggMetricField extends AbstractField { return !isMetricCountable(this.getAggType()); } - async getFieldMetaRequest(config) { - return this._esDocField.getFieldMetaRequest(config); + async getOrdinalFieldMetaRequest(config) { + return this._esDocField.getOrdinalFieldMetaRequest(config); } } diff --git a/x-pack/legacy/plugins/maps/public/layers/fields/es_doc_field.js b/x-pack/legacy/plugins/maps/public/layers/fields/es_doc_field.js index ee082e4546b8b8d..f9baf180dfe5ce9 100644 --- a/x-pack/legacy/plugins/maps/public/layers/fields/es_doc_field.js +++ b/x-pack/legacy/plugins/maps/public/layers/fields/es_doc_field.js @@ -6,6 +6,7 @@ import { AbstractField } from './field'; import { ESTooltipProperty } from '../tooltips/es_tooltip_property'; +import { COLOR_PALETTE_MAX_SIZE } from '../../../common/constants'; export class ESDocField extends AbstractField { static type = 'ES_DOC'; @@ -29,7 +30,7 @@ export class ESDocField extends AbstractField { return true; } - async getFieldMetaRequest(/* config */) { + async getOrdinalFieldMetaRequest() { const field = await this._getField(); if (field.type !== 'number' && field.type !== 'date') { @@ -51,4 +52,29 @@ export class ESDocField extends AbstractField { }, }; } + + async getCategoricalFieldMetaRequest() { + const field = await this._getField(); + if (field.type !== 'string') { + //UX does not support categorical styling for number/date fields + return null; + } + + const topTerms = { + size: COLOR_PALETTE_MAX_SIZE - 1, //need additional color for the "other"-value + }; + if (field.scripted) { + topTerms.script = { + source: field.script, + lang: field.lang, + }; + } else { + topTerms.field = this._fieldName; + } + return { + [this._fieldName]: { + terms: topTerms, + }, + }; + } } diff --git a/x-pack/legacy/plugins/maps/public/layers/fields/field.js b/x-pack/legacy/plugins/maps/public/layers/fields/field.js index f1401a78e217480..b5d157ad1697aa9 100644 --- a/x-pack/legacy/plugins/maps/public/layers/fields/field.js +++ b/x-pack/legacy/plugins/maps/public/layers/fields/field.js @@ -45,7 +45,11 @@ export class AbstractField { return false; } - async getFieldMetaRequest(/* config */) { + async getOrdinalFieldMetaRequest(/* config */) { + return null; + } + + async getCategoricalFieldMetaRequest() { return null; } } diff --git a/x-pack/legacy/plugins/maps/public/layers/layer.js b/x-pack/legacy/plugins/maps/public/layers/layer.js index 21c5f15fb61225a..80da9555b384e4b 100644 --- a/x-pack/legacy/plugins/maps/public/layers/layer.js +++ b/x-pack/legacy/plugins/maps/public/layers/layer.js @@ -344,6 +344,10 @@ export class AbstractLayer { return []; } + async getStringFields() { + return []; + } + async getFields() { return []; } diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/es_search_source/es_search_source.js b/x-pack/legacy/plugins/maps/public/layers/sources/es_search_source/es_search_source.js index 8ef4966e03c1b4c..39015f85ce87551 100644 --- a/x-pack/legacy/plugins/maps/public/layers/sources/es_search_source/es_search_source.js +++ b/x-pack/legacy/plugins/maps/public/layers/sources/es_search_source/es_search_source.js @@ -125,6 +125,21 @@ export class ESSearchSource extends AbstractESSource { } } + async getStringFields() { + try { + const indexPattern = await this.getIndexPattern(); + const aggFields = indexPattern.fields.getByType('string').filter(field => { + return field.aggregatable; + }); + return aggFields.map(field => { + return this.createField({ fieldName: field.name }); + }); + } catch (error) { + //error surfaces in the LayerTOC UI + return []; + } + } + async getFields() { try { const indexPattern = await this.getIndexPattern(); diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/vector_source.js b/x-pack/legacy/plugins/maps/public/layers/sources/vector_source.js index bf7267e9c5858ee..0dec50d77dd703d 100644 --- a/x-pack/legacy/plugins/maps/public/layers/sources/vector_source.js +++ b/x-pack/legacy/plugins/maps/public/layers/sources/vector_source.js @@ -107,6 +107,10 @@ export class AbstractVectorSource extends AbstractSource { return [...(await this.getDateFields()), ...(await this.getNumberFields())]; } + async getStringFields() { + return []; + } + async getLeftJoinFields() { return []; } diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/color_utils.js b/x-pack/legacy/plugins/maps/public/layers/styles/color_utils.js index 8aa32fa7e09c08e..a6188b317573bdb 100644 --- a/x-pack/legacy/plugins/maps/public/layers/styles/color_utils.js +++ b/x-pack/legacy/plugins/maps/public/layers/styles/color_utils.js @@ -12,6 +12,7 @@ import { ColorGradient } from './components/color_gradient'; import { palettes } from '@elastic/eui/lib/services'; import tinycolor from 'tinycolor2'; import chroma from 'chroma-js'; +import { COLOR_PALETTE_MAX_SIZE } from '../../../common/constants'; const GRADIENT_INTERVALS = 8; @@ -84,3 +85,45 @@ export function getLinearGradient(colorStrings) { } return `${linearGradient} ${colorStrings[colorStrings.length - 1]} 100%)`; } + +export const COLOR_PALETTES = [ + { + id: 'palette_0', + colors: DEFAULT_FILL_COLORS.slice(0, COLOR_PALETTE_MAX_SIZE), + }, + { + id: 'palette_1', + colors: [ + '#a6cee3', + '#1f78b4', + '#b2df8a', + '#33a02c', + '#fb9a99', + '#e31a1c', + '#fdbf6f', + '#ff7f00', + '#cab2d6', + '#6a3d9a', + ], + }, + { + id: 'palette_2', + colors: [ + '#8dd3c7', + '#ffffb3', + '#bebada', + '#fb8072', + '#80b1d3', + '#fdb462', + '#b3de69', + '#fccde5', + '#d9d9d9', + '#bc80bd', + ], + }, +]; + +export function getColorPalette(paletteId) { + const palette = COLOR_PALETTES.find(palette => palette.id === paletteId); + return palette ? palette.colors : null; +} diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/color/color_palette_select.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/color/color_palette_select.js new file mode 100644 index 000000000000000..b161e9a24df5809 --- /dev/null +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/color/color_palette_select.js @@ -0,0 +1,130 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React, { Component, Fragment } from 'react'; +import PropTypes from 'prop-types'; + +import { EuiSuperSelect, EuiSpacer } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { ColorStopsCategorical } from './color_stops_categorical'; +import { COLOR_PALETTES } from '../../../color_utils'; +import { COLOR_MAP_TYPE } from '../../../../../../common/constants'; + +const CUSTOM_COLOR_PALETTE = 'CUSTOM_COLOR_PALETTE'; +const CUSTOM_OPTION = { + value: CUSTOM_COLOR_PALETTE, + inputDisplay: ( + + ), +}; + +const colorPaletteInputs = COLOR_PALETTES.map(palette => { + const paletteDisplay = palette.colors.map(color => { + const style = { + backgroundColor: color, + width: '10%', + position: 'relative', + height: '100%', + display: 'inline-block', + }; + // eslint-disable-next-line react/no-danger + return
; + }); + return { + value: palette.id, + inputDisplay:
{paletteDisplay}
, + }; +}); + +export class ColorPaletteSelect extends Component { + state = {}; + + static getDerivedStateFromProps(nextProps, prevState) { + if (nextProps.customColorPalette !== prevState.prevPropsCustomColorPalette) { + return { + prevPropsCustomColorPalette: nextProps.customColorPalette, // reset tracker to latest value + customColorPalette: nextProps.customColorPalette, // reset customColorPalette to latest value + }; + } + + return null; + } + + _onColorPaletteSelect = selectedValue => { + const useCustomColorPalette = selectedValue === CUSTOM_COLOR_PALETTE; + this.props.onChange({ + type: COLOR_MAP_TYPE.CATEGORICAL, + color: useCustomColorPalette ? null : selectedValue, + useCustomColorPalette, + }); + }; + + _onCustomColorPaletteChange = ({ colorStops }) => { + this.props.onChange({ + type: COLOR_MAP_TYPE.CATEGORICAL, + customColorPalette: colorStops, + }); + }; + + render() { + const { + color, + onChange, // eslint-disable-line no-unused-vars + useCustomColorPalette, + customColorPalette, // eslint-disable-line no-unused-vars + ...rest + } = this.props; + + let colorStopsInput; + if (useCustomColorPalette) { + colorStopsInput = ( + + + + + ); + } + + const colorPaletteOptions = [CUSTOM_OPTION, ...colorPaletteInputs]; + let valueOfSelected; + if (useCustomColorPalette) { + valueOfSelected = CUSTOM_COLOR_PALETTE; + } else { + if (colorPaletteOptions.find(option => option.value === color)) { + valueOfSelected = color; + } else { + valueOfSelected = colorPaletteInputs[0].value; + this._onColorPaletteSelect(valueOfSelected); + } + } + + return ( + + + {colorStopsInput} + + ); + } +} + +ColorPaletteSelect.propTypes = { + color: PropTypes.string, + onChange: PropTypes.func.isRequired, + useCustomColorPalette: PropTypes.bool, + customColorPalette: PropTypes.array, +}; diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/color/color_ramp_select.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/color/color_ramp_select.js index c2dd51a0182e34c..f8dd7fa11c27cd1 100644 --- a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/color/color_ramp_select.js +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/color/color_ramp_select.js @@ -10,7 +10,8 @@ import PropTypes from 'prop-types'; import { EuiSuperSelect, EuiSpacer } from '@elastic/eui'; import { COLOR_GRADIENTS } from '../../../color_utils'; import { FormattedMessage } from '@kbn/i18n/react'; -import { ColorStops } from './color_stops'; +import { ColorStopsOrdinal } from './color_stops_ordinal'; +import { COLOR_MAP_TYPE } from '../../../../../../common/constants'; const CUSTOM_COLOR_RAMP = 'CUSTOM_COLOR_RAMP'; @@ -33,6 +34,7 @@ export class ColorRampSelect extends Component { this.props.onChange({ color: useCustomColorRamp ? null : selectedValue, useCustomColorRamp, + type: COLOR_MAP_TYPE.ORDINAL, }); }; @@ -45,6 +47,7 @@ export class ColorRampSelect extends Component { this.props.onChange({ customColorRamp: colorStops, + type: COLOR_MAP_TYPE.ORDINAL, }); }; @@ -62,7 +65,7 @@ export class ColorRampSelect extends Component { colorStopsInput = ( - @@ -82,13 +85,24 @@ export class ColorRampSelect extends Component { }, ...COLOR_GRADIENTS, ]; + let valueOfSelected; + if (useCustomColorRamp) { + valueOfSelected = CUSTOM_COLOR_RAMP; + } else { + if (colorRampOptions.find(option => option.value === color)) { + valueOfSelected = color; + } else { + valueOfSelected = COLOR_GRADIENTS[0].value; + this._onColorRampSelect(valueOfSelected); + } + } return ( diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/color/color_stops_categorical.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/color/color_stops_categorical.js new file mode 100644 index 000000000000000..6c7174612a10765 --- /dev/null +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/color/color_stops_categorical.js @@ -0,0 +1,130 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import _ from 'lodash'; +import React from 'react'; +import PropTypes from 'prop-types'; + +import { EuiFormRow, EuiFieldText, EuiFlexGroup, EuiFlexItem, EuiButtonIcon } from '@elastic/eui'; +import { addCategoricalRow, removeRow, getColorInput, getDeleteButton } from './color_stops_utils'; + +const DEFAULT_COLOR = '#FF0000'; +const DEFAULT_NEXT_COLOR = '#00FF00'; + +export const ColorStopsCategorical = ({ + colorStops = [ + { stop: null, color: DEFAULT_COLOR }, //first stop is the "other" color + { stop: '', color: DEFAULT_NEXT_COLOR }, + ], + onChange, +}) => { + function getStopInput(stop, index) { + const onStopChange = e => { + const newColorStops = _.cloneDeep(colorStops); + newColorStops[index].stop = e.target.value; + onChange({ + colorStops: newColorStops, + isInvalid: false, + }); + }; + + let stopInput; + if (index === 0) { + stopInput = ( + + ); + } else { + stopInput = ( + + ); + } + + return { + stopInput: stopInput, + }; + } + + const rows = colorStops.map((colorStop, index) => { + const { stopInput } = getStopInput(colorStop.stop, index); + const { colorError, colorInput } = getColorInput(colorStops, onChange, colorStop.color, index); + const errors = colorError ? [colorError] : []; + + const onAdd = () => { + const newColorStops = addCategoricalRow(colorStops, index); + onChange({ + colorStops: newColorStops, + isInvalid: false, + }); + }; + + let deleteButton; + if (colorStops.length > 2) { + const onRemove = () => { + const newColorStops = removeRow(colorStops, index); + onChange({ + colorStops: newColorStops, + isInvalid: false, + }); + }; + deleteButton = getDeleteButton(onRemove); + } + + return ( + +
+ + {stopInput} + {colorInput} + +
+ {deleteButton} + +
+
+
+ ); + }); + + return
{rows}
; +}; + +ColorStopsCategorical.propTypes = { + /** + * Array of { stop, color }. + * Stops are numbers in strictly ascending order. + * The range is from the given stop number (inclusive) to the next stop number (exclusive). + * Colors are color hex strings (3 or 6 character). + */ + colorStops: PropTypes.arrayOf( + PropTypes.shape({ + stopKey: PropTypes.number, + color: PropTypes.string, + }) + ), + /** + * Callback for when the color stops changes. Called with { colorStops, isInvalid } + */ + onChange: PropTypes.func.isRequired, +}; diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/color/color_stops.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/color/color_stops_ordinal.js similarity index 80% rename from x-pack/legacy/plugins/maps/public/layers/styles/vector/components/color/color_stops.js rename to x-pack/legacy/plugins/maps/public/layers/styles/vector/components/color/color_stops_ordinal.js index d523cf5870912d1..63fa52d50158f20 100644 --- a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/color/color_stops.js +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/color/color_stops_ordinal.js @@ -16,11 +16,21 @@ import { EuiFlexItem, EuiButtonIcon, } from '@elastic/eui'; -import { addRow, removeRow, isColorInvalid, isStopInvalid, isInvalid } from './color_stops_utils'; +import { + addOrdinalRow, + removeRow, + isColorInvalid, + isOrdinalStopInvalid, + isOrdinalStopsInvalid, + getDeleteButton, +} from './color_stops_utils'; const DEFAULT_COLOR = '#FF0000'; -export const ColorStops = ({ colorStops = [{ stop: 0, color: DEFAULT_COLOR }], onChange }) => { +export const ColorStopsOrdinal = ({ + colorStops = [{ stop: 0, color: DEFAULT_COLOR }], + onChange, +}) => { function getStopInput(stop, index) { const onStopChange = e => { const newColorStops = _.cloneDeep(colorStops); @@ -28,12 +38,12 @@ export const ColorStops = ({ colorStops = [{ stop: 0, color: DEFAULT_COLOR }], o newColorStops[index].stop = isNaN(sanitizedValue) ? '' : sanitizedValue; onChange({ colorStops: newColorStops, - isInvalid: isInvalid(newColorStops), + isInvalid: isOrdinalStopsInvalid(newColorStops), }); }; let error; - if (isStopInvalid(stop)) { + if (isOrdinalStopInvalid(stop)) { error = 'Stop must be a number'; } else if (index !== 0 && colorStops[index - 1].stop >= stop) { error = 'Stop must be greater than previous stop value'; @@ -53,7 +63,7 @@ export const ColorStops = ({ colorStops = [{ stop: 0, color: DEFAULT_COLOR }], o newColorStops[index].color = color; onChange({ colorStops: newColorStops, - isInvalid: isInvalid(newColorStops), + isInvalid: isOrdinalStopsInvalid(newColorStops), }); }; @@ -74,34 +84,25 @@ export const ColorStops = ({ colorStops = [{ stop: 0, color: DEFAULT_COLOR }], o errors.push(colorError); } - const onRemove = () => { - const newColorStops = removeRow(colorStops, index); - onChange({ - colorStops: newColorStops, - isInvalid: isInvalid(newColorStops), - }); - }; - const onAdd = () => { - const newColorStops = addRow(colorStops, index); + const newColorStops = addOrdinalRow(colorStops, index); onChange({ colorStops: newColorStops, - isInvalid: isInvalid(newColorStops), + isInvalid: isOrdinalStopsInvalid(newColorStops), }); }; let deleteButton; if (colorStops.length > 1) { - deleteButton = ( - - ); + const onRemove = () => { + const newColorStops = removeRow(colorStops, index); + onChange({ + colorStops: newColorStops, + isInvalid: isOrdinalStopsInvalid(newColorStops), + }); + }; + deleteButton = getDeleteButton(onRemove); } return ( @@ -135,7 +136,7 @@ export const ColorStops = ({ colorStops = [{ stop: 0, color: DEFAULT_COLOR }], o return
{rows}
; }; -ColorStops.propTypes = { +ColorStopsOrdinal.propTypes = { /** * Array of { stop, color }. * Stops are numbers in strictly ascending order. diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/color/color_stops_utils.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/color/color_stops_utils.js index fb0a25cf7d5ee30..b7dd692952db7fe 100644 --- a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/color/color_stops_utils.js +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/color/color_stops_utils.js @@ -4,7 +4,10 @@ * you may not use this file except in compliance with the Elastic License. */ -import { isValidHex } from '@elastic/eui'; +import { EuiButtonIcon, EuiColorPicker, isValidHex } from '@elastic/eui'; +import _ from 'lodash'; +import React from 'react'; +import { COLOR_MAP_TYPE } from '../../../../../../common/constants'; export function removeRow(colorStops, index) { if (colorStops.length === 1) { @@ -14,7 +17,15 @@ export function removeRow(colorStops, index) { return [...colorStops.slice(0, index), ...colorStops.slice(index + 1)]; } -export function addRow(colorStops, index) { +export function addOrdinalRow(colorStops, index) { + return addRow(colorStops, index, COLOR_MAP_TYPE.ORDINAL); +} + +export function addCategoricalRow(colorStops, index) { + return addRow(colorStops, index, COLOR_MAP_TYPE.CATEGORICAL); +} + +export function addRow(colorStops, index, colorMapType) { const currentStop = colorStops[index].stop; let delta = 1; if (index === colorStops.length - 1) { @@ -29,22 +40,51 @@ export function addRow(colorStops, index) { delta = (nextStop - currentStop) / 2; } + const nextValue = colorMapType === COLOR_MAP_TYPE.ORDINAL ? currentStop + delta : currentStop; const newRow = { - stop: currentStop + delta, + stop: nextValue, color: '#FF0000', }; return [...colorStops.slice(0, index + 1), newRow, ...colorStops.slice(index + 1)]; } +export function getDeleteButton(onRemove) { + return ( + + ); +} + +export function getColorInput(colorStops, onChange, color, index) { + const onColorChange = color => { + const newColorStops = _.cloneDeep(colorStops); + newColorStops[index].color = color; + onChange({ + colorStops: newColorStops, + isInvalid: false, + }); + }; + + return { + colorError: isColorInvalid(color) ? 'Color must provide a valid hex value' : undefined, + colorInput: , + }; +} + export function isColorInvalid(color) { return !isValidHex(color) || color === ''; } -export function isStopInvalid(stop) { +export function isOrdinalStopInvalid(stop) { return stop === '' || isNaN(stop); } -export function isInvalid(colorStops) { +export function isOrdinalStopsInvalid(colorStops) { return colorStops.some((colorStop, index) => { // expect stops to be in ascending order let isDescending = false; @@ -53,6 +93,6 @@ export function isInvalid(colorStops) { isDescending = prevStop >= colorStop.stop; } - return isColorInvalid(colorStop.color) || isStopInvalid(colorStop.stop) || isDescending; + return isColorInvalid(colorStop.color) || isOrdinalStopInvalid(colorStop.stop) || isDescending; }); } diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/color/dynamic_color_form.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/color/dynamic_color_form.js index 5e0f7434b04d08e..546c9394e4865b8 100644 --- a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/color/dynamic_color_form.js +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/color/dynamic_color_form.js @@ -8,55 +8,126 @@ import _ from 'lodash'; import React, { Fragment } from 'react'; import { FieldSelect } from '../field_select'; import { ColorRampSelect } from './color_ramp_select'; +import { ColorPaletteSelect } from './color_palette_select'; import { EuiFlexGroup, EuiFlexItem, EuiSpacer } from '@elastic/eui'; +import { COLOR_MAP_TYPE } from '../../../../../../common/constants'; +import { OrdinalFieldMetaOptionsPopover } from '../ordinal_field_meta_options_popover'; -export function DynamicColorForm({ - fields, - onDynamicStyleChange, - staticDynamicSelect, - styleProperty, -}) { - const styleOptions = styleProperty.getOptions(); - - const onFieldChange = ({ field }) => { - onDynamicStyleChange(styleProperty.getStyleName(), { ...styleOptions, field }); +export class DynamicColorForm extends React.Component { + state = { + colorMapType: COLOR_MAP_TYPE.ORDINAL, }; - const onColorChange = colorOptions => { - onDynamicStyleChange(styleProperty.getStyleName(), { - ...styleOptions, - ...colorOptions, - }); - }; + constructor() { + super(); + this._isMounted = false; + } - let colorRampSelect; - if (styleOptions.field && styleOptions.field.name) { - colorRampSelect = ( - - ); + componentWillUnmount() { + this._isMounted = false; + } + + componentDidMount() { + this._isMounted = true; + this._loadColorMapType(); } - return ( - - - {staticDynamicSelect} - - { + const options = { + ...this.props.styleProperty.getOptions(), + fieldMetaOptions, + }; + this.props.onDynamicStyleChange(this.props.styleProperty.getStyleName(), options); + }; + + _getColorSelector() { + const { onDynamicStyleChange, styleProperty } = this.props; + const styleOptions = styleProperty.getOptions(); + + let colorSelect; + if (styleOptions.field && styleOptions.field.name) { + const onColorChange = colorOptions => { + onDynamicStyleChange(styleProperty.getStyleName(), { + ...styleOptions, + ...colorOptions, + }); + }; + if (this.state.colorMapType === COLOR_MAP_TYPE.ORDINAL) { + colorSelect = ( + onColorChange(options)} + color={styleOptions.color} + customColorRamp={styleOptions.customColorRamp} + useCustomColorRamp={_.get(styleOptions, 'useCustomColorRamp', false)} + compressed + /> + ); + } else if (this.state.colorMapType === COLOR_MAP_TYPE.CATEGORICAL) { + colorSelect = ( + onColorChange(options)} + color={styleOptions.color} + customColorPalette={styleOptions.customColorPalette} + useCustomColorPalette={_.get(styleOptions, 'useCustomColorPalette', false)} compressed /> - - - - {colorRampSelect} - - ); + ); + } + return colorSelect; + } + } + + render() { + const { fields, onDynamicStyleChange, staticDynamicSelect, styleProperty } = this.props; + const styleOptions = styleProperty.getOptions(); + const onFieldChange = ({ field }) => { + onDynamicStyleChange(styleProperty.getStyleName(), { ...styleOptions, field }); + }; + + const colorSelect = this._getColorSelector(); + + const fieldMetaOptionsPopover = + this.state.colorMapType === COLOR_MAP_TYPE.ORDINAL && styleProperty.supportsFieldMeta() ? ( + + ) : null; + + return ( + + + {staticDynamicSelect} + + + + + + {colorSelect} + {fieldMetaOptionsPopover} + + ); + } } diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/legend/extract_color_from_style_property.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/legend/extract_color_from_style_property.js index 157b863ac498673..3b11c7b77a237a6 100644 --- a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/legend/extract_color_from_style_property.js +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/legend/extract_color_from_style_property.js @@ -5,7 +5,8 @@ */ import { VectorStyle } from '../../vector_style'; -import { getColorRampCenterColor } from '../../../color_utils'; +import { getColorRampCenterColor, getColorPalette } from '../../../color_utils'; +import { COLOR_MAP_TYPE } from '../../../../../../common/constants'; export function extractColorFromStyleProperty(colorStyleProperty, defaultColor) { if (!colorStyleProperty) { @@ -21,19 +22,30 @@ export function extractColorFromStyleProperty(colorStyleProperty, defaultColor) return defaultColor; } - // return middle of gradient for dynamic style property + if (colorStyleProperty.options.type === COLOR_MAP_TYPE.CATEGORICAL) { + if (colorStyleProperty.options.useCustomColorPalette) { + return colorStyleProperty.options.customColorPalette && + colorStyleProperty.options.customColorPalette.length + ? colorStyleProperty.options.customColorPalette[0].color + : defaultColor; + } - if (colorStyleProperty.options.useCustomColorRamp) { - if ( - !colorStyleProperty.options.customColorRamp || - !colorStyleProperty.options.customColorRamp.length - ) { - return defaultColor; + const palette = getColorPalette(colorStyleProperty.options.color); + return palette[0]; + } else { + // return middle of gradient for dynamic style property + if (colorStyleProperty.options.useCustomColorRamp) { + if ( + !colorStyleProperty.options.customColorRamp || + !colorStyleProperty.options.customColorRamp.length + ) { + return defaultColor; + } + // favor the lowest color in even arrays + const middleIndex = Math.floor((colorStyleProperty.options.customColorRamp.length - 1) / 2); + return colorStyleProperty.options.customColorRamp[middleIndex].color; } - // favor the lowest color in even arrays - const middleIndex = Math.floor((colorStyleProperty.options.customColorRamp.length - 1) / 2); - return colorStyleProperty.options.customColorRamp[middleIndex].color; - } - return getColorRampCenterColor(colorStyleProperty.options.color); + return getColorRampCenterColor(colorStyleProperty.options.color); + } } diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/field_meta_options_popover.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/ordinal_field_meta_options_popover.js similarity index 98% rename from x-pack/legacy/plugins/maps/public/layers/styles/vector/components/field_meta_options_popover.js rename to x-pack/legacy/plugins/maps/public/layers/styles/vector/components/ordinal_field_meta_options_popover.js index 471403e1f3999d6..dee333f1639607f 100644 --- a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/field_meta_options_popover.js +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/ordinal_field_meta_options_popover.js @@ -31,7 +31,7 @@ function getIsEnableToggleLabel(styleName) { } } -export class FieldMetaOptionsPopover extends Component { +export class OrdinalFieldMetaOptionsPopover extends Component { state = { isPopoverOpen: false, }; diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/size/dynamic_size_form.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/size/dynamic_size_form.js index 8b069cd53b731ac..2f3a80684b3b140 100644 --- a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/size/dynamic_size_form.js +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/size/dynamic_size_form.js @@ -9,6 +9,7 @@ import React, { Fragment } from 'react'; import { FieldSelect } from '../field_select'; import { SizeRangeSelector } from './size_range_selector'; import { EuiFlexGroup, EuiFlexItem, EuiSpacer } from '@elastic/eui'; +import { OrdinalFieldMetaOptionsPopover } from '../ordinal_field_meta_options_popover'; export function DynamicSizeForm({ fields, @@ -22,6 +23,14 @@ export function DynamicSizeForm({ onDynamicStyleChange(styleProperty.getStyleName(), { ...styleOptions, field }); }; + const onFieldMetaOptionsChange = fieldMetaOptions => { + const options = { + ...styleProperty.getOptions(), + fieldMetaOptions, + }; + onDynamicStyleChange(styleProperty.getStyleName(), options); + }; + const onSizeRangeChange = ({ minSize, maxSize }) => { onDynamicStyleChange(styleProperty.getStyleName(), { ...styleOptions, @@ -43,6 +52,13 @@ export function DynamicSizeForm({ ); } + const fieldMetaOptionsPopover = styleProperty.supportsFieldMeta() ? ( + + ) : null; + return ( @@ -58,6 +74,7 @@ export function DynamicSizeForm({ {sizeRange} + {fieldMetaOptionsPopover} ); } diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/style_prop_editor.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/style_prop_editor.js index 1ac8edfb2cc694d..ed80e94358a106a 100644 --- a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/style_prop_editor.js +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/style_prop_editor.js @@ -5,7 +5,6 @@ */ import React, { Component, Fragment } from 'react'; -import { FieldMetaOptionsPopover } from './field_meta_options_popover'; import { getVectorStyleLabel } from './get_vector_style_label'; import { EuiFormRow, EuiSelect } from '@elastic/eui'; import { VectorStyle } from '../vector_style'; @@ -35,14 +34,6 @@ export class StylePropEditor extends Component { } }; - _onFieldMetaOptionsChange = fieldMetaOptions => { - const options = { - ...this.props.styleProperty.getOptions(), - fieldMetaOptions, - }; - this.props.onDynamicStyleChange(this.props.styleProperty.getStyleName(), options); - }; - renderStaticDynamicSelect() { const options = [ { @@ -80,13 +71,6 @@ export class StylePropEditor extends Component { } render() { - const fieldMetaOptionsPopover = this.props.styleProperty.isDynamic() ? ( - - ) : null; - return ( ); diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/vector_style_editor.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/vector_style_editor.js index 8e80e036dbb8bc0..d65a75a795738ba 100644 --- a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/vector_style_editor.js +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/vector_style_editor.js @@ -31,6 +31,7 @@ export class VectorStyleEditor extends Component { state = { dateFields: [], numberFields: [], + stringFields: [], fields: [], defaultDynamicProperties: getDefaultDynamicProperties(), defaultStaticProperties: getDefaultStaticProperties(), @@ -76,6 +77,13 @@ export class VectorStyleEditor extends Component { this.setState({ numberFields: numberFieldsArray }); } + const stringFields = await this.props.layer.getStringFields(); + const stringFieldPromises = stringFields.map(getFieldMeta); + const stringFieldsArray = await Promise.all(stringFieldPromises); + if (this._isMounted && !_.isEqual(stringFieldsArray, this.state.stringFields)) { + this.setState({ stringFields: stringFieldsArray }); + } + const fields = await this.props.layer.getFields(); const fieldPromises = fields.map(getFieldMeta); const fieldsArray = await Promise.all(fieldPromises); @@ -125,6 +133,10 @@ export class VectorStyleEditor extends Component { return [...this.state.dateFields, ...this.state.numberFields]; } + _getOrdinalAndStringFields() { + return [...this.state.dateFields, ...this.state.numberFields, ...this.state.stringFields]; + } + _handleSelectedFeatureChange = selectedFeature => { this.setState({ selectedFeature }); }; @@ -156,7 +168,7 @@ export class VectorStyleEditor extends Component { onStaticStyleChange={this._onStaticStyleChange} onDynamicStyleChange={this._onDynamicStyleChange} styleProperty={this.props.styleProperties[VECTOR_STYLES.FILL_COLOR]} - fields={this._getOrdinalFields()} + fields={this._getOrdinalAndStringFields()} defaultStaticStyleOptions={ this.state.defaultStaticProperties[VECTOR_STYLES.FILL_COLOR].options } @@ -174,7 +186,7 @@ export class VectorStyleEditor extends Component { onStaticStyleChange={this._onStaticStyleChange} onDynamicStyleChange={this._onDynamicStyleChange} styleProperty={this.props.styleProperties[VECTOR_STYLES.LINE_COLOR]} - fields={this._getOrdinalFields()} + fields={this._getOrdinalAndStringFields()} defaultStaticStyleOptions={ this.state.defaultStaticProperties[VECTOR_STYLES.LINE_COLOR].options } @@ -241,7 +253,7 @@ export class VectorStyleEditor extends Component { onStaticStyleChange={this._onStaticStyleChange} onDynamicStyleChange={this._onDynamicStyleChange} styleProperty={this.props.styleProperties[VECTOR_STYLES.LABEL_COLOR]} - fields={this._getOrdinalFields()} + fields={this._getOrdinalAndStringFields()} defaultStaticStyleOptions={ this.state.defaultStaticProperties[VECTOR_STYLES.LABEL_COLOR].options } diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/dynamic_color_property.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/dynamic_color_property.js index 200df9e5cc33d30..7b4965186505a8b 100644 --- a/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/dynamic_color_property.js +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/dynamic_color_property.js @@ -7,12 +7,20 @@ import { DynamicStyleProperty } from './dynamic_style_property'; import _ from 'lodash'; import { getComputedFieldName } from '../style_util'; -import { getColorRampStops } from '../../color_utils'; +import { getColorRampStops, getColorPalette } from '../../color_utils'; import { ColorGradient } from '../../components/color_gradient'; import React from 'react'; -import { EuiFlexGroup, EuiFlexItem, EuiSpacer, EuiText, EuiToolTip } from '@elastic/eui'; +import { + EuiFlexGroup, + EuiFlexItem, + EuiSpacer, + EuiText, + EuiToolTip, + EuiTextColor, +} from '@elastic/eui'; import { VectorIcon } from '../components/legend/vector_icon'; import { VECTOR_STYLES } from '../vector_style_defaults'; +import { COLOR_MAP_TYPE } from '../../../../../common/constants'; export class DynamicColorProperty extends DynamicStyleProperty { syncCircleColorWithMb(mbLayerId, mbMap, alpha) { @@ -55,7 +63,17 @@ export class DynamicColorProperty extends DynamicStyleProperty { mbMap.setPaintProperty(mbLayerId, 'text-opacity', alpha); } - isCustomColorRamp() { + isOrdinal() { + return ( + typeof this._options.type === 'undefined' || this._options.type === COLOR_MAP_TYPE.ORDINAL + ); + } + + isCategorical() { + return this._options.type === COLOR_MAP_TYPE.CATEGORICAL; + } + + isCustomOrdinalColorRamp() { return this._options.useCustomColorRamp; } @@ -63,16 +81,16 @@ export class DynamicColorProperty extends DynamicStyleProperty { return true; } - isScaled() { - return !this.isCustomColorRamp(); + isOrdinalScaled() { + return this.isOrdinal() && !this.isCustomOrdinalColorRamp(); } - isRanged() { - return !this.isCustomColorRamp(); + isOrdinalRanged() { + return this.isOrdinal() && !this.isCustomOrdinalColorRamp(); } - hasBreaks() { - return this.isCustomColorRamp(); + hasOrdinalBreaks() { + return (this.isOrdinal() && this.isCustomOrdinalColorRamp()) || this.isCategorical(); } _getMbColor() { @@ -82,6 +100,12 @@ export class DynamicColorProperty extends DynamicStyleProperty { return null; } + return this._getMBDataDrivenColor({ + targetName: getComputedFieldName(this._styleName, this._options.field.name), + }); + } + + _getMbDataDrivenOrdinalColor({ targetName }) { if ( this._options.useCustomColorRamp && (!this._options.customColorRamp || !this._options.customColorRamp.length) @@ -89,15 +113,8 @@ export class DynamicColorProperty extends DynamicStyleProperty { return null; } - return this._getMBDataDrivenColor({ - targetName: getComputedFieldName(this._styleName, this._options.field.name), - colorStops: this._getMBColorStops(), - isSteps: this._options.useCustomColorRamp, - }); - } - - _getMBDataDrivenColor({ targetName, colorStops, isSteps }) { - if (isSteps) { + const colorStops = this._getMbOrdinalColorStops(); + if (this._options.useCustomColorRamp) { const firstStopValue = colorStops[0]; const lessThenFirstStopValue = firstStopValue - 1; return [ @@ -107,7 +124,6 @@ export class DynamicColorProperty extends DynamicStyleProperty { ...colorStops, ]; } - return [ 'interpolate', ['linear'], @@ -118,14 +134,75 @@ export class DynamicColorProperty extends DynamicStyleProperty { ]; } - _getMBColorStops() { - if (this._options.useCustomColorRamp) { - return this._options.customColorRamp.reduce((accumulatedStops, nextStop) => { - return [...accumulatedStops, nextStop.stop, nextStop.color]; - }, []); + _getColorPaletteStops() { + if (this._options.useCustomColorPalette && this._options.customColorPalette) { + return this._options.customColorPalette; + } + + const fieldMeta = this.getFieldMeta(); + if (!fieldMeta || !fieldMeta.categories) { + return []; + } + + const colors = getColorPalette(this._options.color); + if (!colors) { + return []; + } + const maxLength = Math.min(colors.length, fieldMeta.categories.length + 1); + + const stops = []; + for (let i = 0; i < maxLength; i++) { + stops.push({ + stop: i === 0 ? '__DEFAULT__' : fieldMeta.categories[i - 1].key, + isDefault: i === 0, + color: colors[i], + }); + } + return stops; + } + + _getMbDataDrivenCategoricalColor() { + if ( + this._options.useCustomColorPalette && + (!this._options.customColorPalette || !this._options.customColorPalette.length) + ) { + return null; } - return getColorRampStops(this._options.color); + const paletteStops = this._getColorPaletteStops(); + if (!paletteStops.length) { + return null; + } + const mbStops = []; + for (let i = 1; i < paletteStops.length; i++) { + const stop = paletteStops[i]; + mbStops.push(stop.stop); + mbStops.push(stop.color); + } + mbStops.push(paletteStops[0].color); //first color is default color + return ['match', ['get', this._options.field.name], ...mbStops]; + } + + _getMBDataDrivenColor({ targetName }) { + if (this.isCategorical()) { + return this._getMbDataDrivenCategoricalColor({ targetName }); + } else { + return this._getMbDataDrivenOrdinalColor({ targetName }); + } + } + + _getOrdinalColorStopsFromCustom() { + return this._options.customColorRamp.reduce((accumulatedStops, nextStop) => { + return [...accumulatedStops, nextStop.stop, nextStop.color]; + }, []); + } + + _getMbOrdinalColorStops() { + if (this._options.useCustomColorRamp) { + return this._getOrdinalColorStopsFromCustom(); + } else { + return getColorRampStops(this._options.color); + } } renderRangeLegendHeader() { @@ -172,19 +249,43 @@ export class DynamicColorProperty extends DynamicStyleProperty { ); } - _renderColorbreaks({ isLinesOnly, isPointsOnly, symbolId }) { - if (!this._options.customColorRamp) { - return null; + _getColorRampStops() { + if (this._options.useCustomColorRamp && this._options.customColorRamp) { + return this._options.customColorRamp; + } else { + return []; + } + } + + _getColorStops() { + if (this.isOrdinal()) { + return this._getColorRampStops(); + } else if (this.isCategorical()) { + return this._getColorPaletteStops(); + } else { + return []; } + } + + _renderColorbreaks({ isLinesOnly, isPointsOnly, symbolId }) { + const stops = this._getColorStops(); + return stops.map((config, index) => { + let textValue; + if (config.isDefault) { + textValue = ( + + Default + + ); + } else { + const value = this.formatField(config.stop); + textValue = {value}; + } - return this._options.customColorRamp.map((config, index) => { - const value = this.formatField(config.stop); return ( - - {value} - + {textValue} {this._renderStopIcon(config.color, isLinesOnly, isPointsOnly, symbolId)} diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/dynamic_orientation_property.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/dynamic_orientation_property.js index 5b6f494600c2a95..1d2457142c0820c 100644 --- a/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/dynamic_orientation_property.js +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/dynamic_orientation_property.js @@ -26,7 +26,7 @@ export class DynamicOrientationProperty extends DynamicStyleProperty { return false; } - isScaled() { + isOrdinalScaled() { return false; } } diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/dynamic_style_property.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/dynamic_style_property.js index bac3c96581967c7..5a9ba1d6f03c280 100644 --- a/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/dynamic_style_property.js +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/dynamic_style_property.js @@ -7,7 +7,7 @@ import _ from 'lodash'; import { AbstractStyleProperty } from './style_property'; import { DEFAULT_SIGMA } from '../vector_style_defaults'; -import { STYLE_TYPE } from '../../../../../common/constants'; +import { COLOR_PALETTE_MAX_SIZE, STYLE_TYPE } from '../../../../../common/constants'; import { scaleValue } from '../style_util'; import React from 'react'; import { OrdinalLegend } from './components/ordinal_legend'; @@ -39,11 +39,15 @@ export class DynamicStyleProperty extends AbstractStyleProperty { return true; } - hasBreaks() { + isCategorical() { return false; } - isRanged() { + hasOrdinalBreaks() { + return false; + } + + isOrdinalRanged() { return true; } @@ -61,21 +65,33 @@ export class DynamicStyleProperty extends AbstractStyleProperty { } supportsFieldMeta() { - return this.isComplete() && this.isScaled() && this._field.supportsFieldMeta(); + if (this.isOrdinal()) { + return this.isComplete() && this.isOrdinalScaled() && this._field.supportsFieldMeta(); + } else if (this.isCategorical()) { + return this.isComplete() && this._field.supportsFieldMeta(); + } else { + return false; + } } async getFieldMetaRequest() { - const fieldMetaOptions = this.getFieldMetaOptions(); - return this._field.getFieldMetaRequest({ - sigma: _.get(fieldMetaOptions, 'sigma', DEFAULT_SIGMA), - }); + if (this.isOrdinal()) { + const fieldMetaOptions = this.getFieldMetaOptions(); + return this._field.getOrdinalFieldMetaRequest({ + sigma: _.get(fieldMetaOptions, 'sigma', DEFAULT_SIGMA), + }); + } else if (this.isCategorical()) { + return this._field.getCategoricalFieldMetaRequest(); + } else { + return null; + } } supportsFeatureState() { return true; } - isScaled() { + isOrdinalScaled() { return true; } @@ -83,11 +99,7 @@ export class DynamicStyleProperty extends AbstractStyleProperty { return _.get(this.getOptions(), 'fieldMetaOptions', {}); } - pluckStyleMetaFromFeatures(features) { - if (!this.isOrdinal()) { - return null; - } - + _pluckOrdinalStyleMetaFromFeatures(features) { const name = this.getField().getName(); let min = Infinity; let max = -Infinity; @@ -109,11 +121,44 @@ export class DynamicStyleProperty extends AbstractStyleProperty { }; } - pluckStyleMetaFromFieldMetaData(fieldMetaData) { - if (!this.isOrdinal()) { + _pluckCategoricalStyleMetaFromFeatures(features) { + const fieldName = this.getField().getName(); + const counts = new Map(); + for (let i = 0; i < features.length; i++) { + const feature = features[i]; + const term = feature.properties[fieldName]; + if (counts.has(term)) { + counts.set(term, counts.get(term) + 1); + } else { + counts.set(term, 1); + } + } + + const ordered = []; + for (const [key, value] of counts) { + ordered.push({ key, count: value }); + } + + ordered.sort((a, b) => { + return a.count - b.count; + }); + const truncated = ordered.slice(0, COLOR_PALETTE_MAX_SIZE); + return { + categories: truncated, + }; + } + + pluckStyleMetaFromFeatures(features) { + if (this.isOrdinal()) { + return this._pluckOrdinalStyleMetaFromFeatures(features); + } else if (this.isCategorical()) { + return this._pluckCategoricalStyleMetaFromFeatures(features); + } else { return null; } + } + _pluckOrdinalStyleMetaFromFieldMetaData(fieldMetaData) { const realFieldName = this._field.getESDocFieldName ? this._field.getESDocFieldName() : this._field.getName(); @@ -136,6 +181,33 @@ export class DynamicStyleProperty extends AbstractStyleProperty { }; } + _pluckCategoricalStyleMetaFromFieldMetaData(fieldMetaData) { + const name = this.getField().getName(); + if (!fieldMetaData[name] || !fieldMetaData[name].buckets) { + return null; + } + + const ordered = fieldMetaData[name].buckets.map(bucket => { + return { + key: bucket.key, + count: bucket.doc_count, + }; + }); + return { + categories: ordered, + }; + } + + pluckStyleMetaFromFieldMetaData(fieldMetaData) { + if (this.isOrdinal()) { + return this._pluckOrdinalStyleMetaFromFieldMetaData(fieldMetaData); + } else if (this.isCategorical()) { + return this._pluckCategoricalStyleMetaFromFieldMetaData(fieldMetaData); + } else { + return null; + } + } + formatField(value) { if (this.getField()) { const fieldName = this.getField().getName(); @@ -152,7 +224,7 @@ export class DynamicStyleProperty extends AbstractStyleProperty { } const valueAsFloat = parseFloat(value); - if (this.isScaled()) { + if (this.isOrdinalScaled()) { return scaleValue(valueAsFloat, this.getFieldMeta()); } if (isNaN(valueAsFloat)) { @@ -181,9 +253,15 @@ export class DynamicStyleProperty extends AbstractStyleProperty { } renderLegendDetailRow({ loadIsPointsOnly, loadIsLinesOnly, symbolId }) { - if (this.isRanged()) { - return this._renderRangeLegend(); - } else if (this.hasBreaks()) { + if (this.isOrdinal()) { + if (this.isOrdinalRanged()) { + return this._renderRangeLegend(); + } else if (this.hasOrdinalBreaks()) { + return this._renderCategoricalLegend({ loadIsPointsOnly, loadIsLinesOnly, symbolId }); + } else { + return null; + } + } else if (this.isCategorical()) { return this._renderCategoricalLegend({ loadIsPointsOnly, loadIsLinesOnly, symbolId }); } else { return null; diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/dynamic_text_property.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/dynamic_text_property.js index fbc4c3af78f9831..6a40a80a1a7a665 100644 --- a/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/dynamic_text_property.js +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/dynamic_text_property.js @@ -29,7 +29,7 @@ export class DynamicTextProperty extends DynamicStyleProperty { return false; } - isScaled() { + isOrdinalScaled() { return false; } } diff --git a/x-pack/legacy/plugins/maps/public/layers/vector_layer.js b/x-pack/legacy/plugins/maps/public/layers/vector_layer.js index dd9a1b7a14c10de..54618dc8d5b4462 100644 --- a/x-pack/legacy/plugins/maps/public/layers/vector_layer.js +++ b/x-pack/legacy/plugins/maps/public/layers/vector_layer.js @@ -213,6 +213,10 @@ export class VectorLayer extends AbstractLayer { return [...(await this.getDateFields()), ...(await this.getNumberFields())]; } + async getStringFields() { + return await this._source.getStringFields(); + } + async getFields() { const sourceFields = await this._source.getFields(); return [...sourceFields, ...this._getJoinFields()];