diff --git a/x-pack/legacy/plugins/maps/common/constants.js b/x-pack/legacy/plugins/maps/common/constants.js
index 6e7776d43f4d4..ab8b853377b3b 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 189aad3785034..65109cb99809f 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 ee082e4546b8b..f9baf180dfe5c 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 f1401a78e2174..b5d157ad1697a 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 21c5f15fb6122..80da9555b384e 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 8ef4966e03c1b..39015f85ce875 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 bf7267e9c5858..0dec50d77dd70 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 8aa32fa7e09c0..a6188b317573b 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 0000000000000..b161e9a24df58
--- /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 c2dd51a0182e3..f8dd7fa11c27c 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 0000000000000..6c7174612a107
--- /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 d523cf5870912..63fa52d50158f 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 fb0a25cf7d5ee..b7dd692952db7 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 5e0f7434b04d0..546c9394e4865 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 157b863ac4986..3b11c7b77a237 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 471403e1f3999..dee333f163960 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 8b069cd53b731..2f3a80684b3b1 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 1ac8edfb2cc69..ed80e94358a10 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 8e80e036dbb8b..d65a75a795738 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 200df9e5cc33d..27b06810c1439 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,81 @@ 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.map((config, index) => {
+ return {
+ stop: config.stop,
+ color: config.color,
+ isDefault: index === 0,
+ };
+ });
+ }
+
+ 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 +255,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 5b6f494600c2a..1d2457142c082 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 bac3c96581967..5a9ba1d6f03c2 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 fbc4c3af78f98..6a40a80a1a7a6 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 dd9a1b7a14c10..54618dc8d5b44 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()];