diff --git a/x-pack/plugins/maps/common/constants.ts b/x-pack/plugins/maps/common/constants.ts index 139f9e88094e0..613e507459389 100644 --- a/x-pack/plugins/maps/common/constants.ts +++ b/x-pack/plugins/maps/common/constants.ts @@ -170,10 +170,10 @@ export enum LAYER_STYLE_TYPE { TILE = 'TILE', } -export const COLOR_MAP_TYPE = { - CATEGORICAL: 'CATEGORICAL', - ORDINAL: 'ORDINAL', -}; +export enum COLOR_MAP_TYPE { + CATEGORICAL = 'CATEGORICAL', + ORDINAL = 'ORDINAL', +} export const CATEGORICAL_DATA_TYPES = ['string', 'ip', 'boolean']; export const ORDINAL_DATA_TYPES = ['number', 'date']; diff --git a/x-pack/plugins/maps/common/descriptor_types/style_property_descriptor_types.d.ts b/x-pack/plugins/maps/common/descriptor_types/style_property_descriptor_types.d.ts index 381bc5bba01c0..4846054ca26cb 100644 --- a/x-pack/plugins/maps/common/descriptor_types/style_property_descriptor_types.d.ts +++ b/x-pack/plugins/maps/common/descriptor_types/style_property_descriptor_types.d.ts @@ -6,6 +6,7 @@ /* eslint-disable @typescript-eslint/consistent-type-definitions */ import { + COLOR_MAP_TYPE, FIELD_ORIGIN, LABEL_BORDER_SIZES, SYMBOLIZE_AS_TYPES, @@ -60,7 +61,7 @@ export type IconStop = { export type ColorDynamicOptions = { // ordinal color properties - color: string; // TODO move color category ramps to constants and make ENUM type + color?: string; // TODO move color category ramps to constants and make ENUM type customColorRamp?: OrdinalColorStop[]; useCustomColorRamp?: boolean; @@ -71,21 +72,27 @@ export type ColorDynamicOptions = { field?: StylePropertyField; fieldMetaOptions: FieldMetaOptions; + + type?: COLOR_MAP_TYPE; }; export type ColorStaticOptions = { color: string; }; +export type ColorStaticStylePropertyDescriptor = { + type: STYLE_TYPE.STATIC; + options: ColorStaticOptions; +}; + +export type ColorDynamicStylePropertyDescriptor = { + type: STYLE_TYPE.DYNAMIC; + options: ColorDynamicOptions; +}; + export type ColorStylePropertyDescriptor = - | { - type: STYLE_TYPE.STATIC; - options: ColorStaticOptions; - } - | { - type: STYLE_TYPE.DYNAMIC; - options: ColorDynamicOptions; - }; + | ColorStaticStylePropertyDescriptor + | ColorDynamicStylePropertyDescriptor; export type IconDynamicOptions = { iconPaletteId?: string; diff --git a/x-pack/plugins/maps/public/classes/styles/vector/components/legend/extract_color_from_style_property.js b/x-pack/plugins/maps/public/classes/styles/vector/components/legend/extract_color_from_style_property.js deleted file mode 100644 index 66a93c7301318..0000000000000 --- a/x-pack/plugins/maps/public/classes/styles/vector/components/legend/extract_color_from_style_property.js +++ /dev/null @@ -1,57 +0,0 @@ -/* - * 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 { getColorRampCenterColor, getColorPalette } from '../../../color_utils'; -import { COLOR_MAP_TYPE, STYLE_TYPE } from '../../../../../../common/constants'; - -export function extractColorFromStyleProperty(colorStyleProperty, defaultColor) { - if (!colorStyleProperty) { - return defaultColor; - } - - if (colorStyleProperty.type === STYLE_TYPE.STATIC) { - return colorStyleProperty.options.color; - } - - // Do not use dynamic color unless configuration is complete - if (!colorStyleProperty.options.field || !colorStyleProperty.options.field.name) { - return defaultColor; - } - - if (colorStyleProperty.options.type === COLOR_MAP_TYPE.CATEGORICAL) { - if (colorStyleProperty.options.useCustomColorPalette) { - return colorStyleProperty.options.customColorPalette && - colorStyleProperty.options.customColorPalette.length - ? colorStyleProperty.options.customColorPalette[0].colorCategory - : defaultColor; - } - - if (!colorStyleProperty.options.colorCategory) { - return null; - } - - const palette = getColorPalette(colorStyleProperty.options.colorCategory); - 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; - } - - if (!colorStyleProperty.options.color) { - return null; - } - return getColorRampCenterColor(colorStyleProperty.options.color); - } -} diff --git a/x-pack/plugins/maps/public/classes/styles/vector/components/legend/extract_color_from_style_property.test.ts b/x-pack/plugins/maps/public/classes/styles/vector/components/legend/extract_color_from_style_property.test.ts new file mode 100644 index 0000000000000..a14956093b3b3 --- /dev/null +++ b/x-pack/plugins/maps/public/classes/styles/vector/components/legend/extract_color_from_style_property.test.ts @@ -0,0 +1,198 @@ +/* + * 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 { extractColorFromStyleProperty } from './extract_color_from_style_property'; +import { + ColorDynamicOptions, + ColorDynamicStylePropertyDescriptor, + ColorStaticOptions, + ColorStaticStylePropertyDescriptor, +} from '../../../../../../common/descriptor_types'; +import { COLOR_MAP_TYPE, FIELD_ORIGIN, STYLE_TYPE } from '../../../../../../common/constants'; +// @ts-ignore +import { euiPaletteColorBlind } from '@elastic/eui/lib/services'; + +const blue = '#0000ff'; +const yellow = '#ffff00'; +const green = '#00FF00'; +const purple = '#800080'; +const defaultColor = '#FFFFFF'; +const fieldMetaOptions = { + isEnabled: true, +}; +const field = { + name: 'myField', + origin: FIELD_ORIGIN.SOURCE, +}; + +describe('static', () => { + test('Should return color', () => { + const colorStyleProperty = { + type: STYLE_TYPE.STATIC, + options: { + color: blue, + } as ColorStaticOptions, + } as ColorStaticStylePropertyDescriptor; + expect(extractColorFromStyleProperty(colorStyleProperty, defaultColor)).toBe(blue); + }); +}); + +describe('dynamic', () => { + test('Should return default color when field is not provided', () => { + const colorStyleProperty = { + type: STYLE_TYPE.DYNAMIC, + options: { + fieldMetaOptions, + } as ColorDynamicOptions, + } as ColorDynamicStylePropertyDescriptor; + expect(extractColorFromStyleProperty(colorStyleProperty, defaultColor)).toBe(defaultColor); + }); + + describe('categorical', () => { + describe('predefined color palette', () => { + test('Should return default color when color palette is not specified', () => { + const colorStyleProperty = { + type: STYLE_TYPE.DYNAMIC, + options: { + type: COLOR_MAP_TYPE.CATEGORICAL, + field, + fieldMetaOptions, + } as ColorDynamicOptions, + } as ColorDynamicStylePropertyDescriptor; + expect(extractColorFromStyleProperty(colorStyleProperty, defaultColor)).toBe(defaultColor); + }); + + test('Should return first color from color palette', () => { + const colorStyleProperty = { + type: STYLE_TYPE.DYNAMIC, + options: { + type: COLOR_MAP_TYPE.CATEGORICAL, + colorCategory: 'palette_0', + field, + fieldMetaOptions, + } as ColorDynamicOptions, + } as ColorDynamicStylePropertyDescriptor; + expect(extractColorFromStyleProperty(colorStyleProperty, defaultColor)).toBe( + euiPaletteColorBlind()[0] + ); + }); + }); + + describe('custom color palette', () => { + test('Should return default color when custom color palette is not provided', () => { + const colorStyleProperty = { + type: STYLE_TYPE.DYNAMIC, + options: { + type: COLOR_MAP_TYPE.CATEGORICAL, + colorCategory: 'palette_0', + field, + fieldMetaOptions, + useCustomColorPalette: true, + } as ColorDynamicOptions, + } as ColorDynamicStylePropertyDescriptor; + expect(extractColorFromStyleProperty(colorStyleProperty, defaultColor)).toBe(defaultColor); + }); + + test('Should return first color from custom color palette', () => { + const colorStyleProperty = { + type: STYLE_TYPE.DYNAMIC, + options: { + type: COLOR_MAP_TYPE.CATEGORICAL, + colorCategory: 'palette_0', + field, + fieldMetaOptions, + useCustomColorPalette: true, + customColorPalette: [{ stop: 'myCategory1', color: blue }], + } as ColorDynamicOptions, + } as ColorDynamicStylePropertyDescriptor; + expect(extractColorFromStyleProperty(colorStyleProperty, defaultColor)).toBe(blue); + }); + }); + }); + + describe('ordinal', () => { + describe('predefined color ramp', () => { + test('Should return default color when color ramp is not specified', () => { + const colorStyleProperty = { + type: STYLE_TYPE.DYNAMIC, + options: { + type: COLOR_MAP_TYPE.ORDINAL, + field, + fieldMetaOptions, + } as ColorDynamicOptions, + } as ColorDynamicStylePropertyDescriptor; + expect(extractColorFromStyleProperty(colorStyleProperty, defaultColor)).toBe(defaultColor); + }); + + test('Should return center color from color ramp', () => { + const colorStyleProperty = { + type: STYLE_TYPE.DYNAMIC, + options: { + type: COLOR_MAP_TYPE.ORDINAL, + color: 'Blues', + field, + fieldMetaOptions, + } as ColorDynamicOptions, + } as ColorDynamicStylePropertyDescriptor; + expect(extractColorFromStyleProperty(colorStyleProperty, defaultColor)).toBe( + 'rgb(106,173,213)' + ); + }); + }); + + describe('custom color ramp', () => { + test('Should return default color when custom color ramp is not provided', () => { + const colorStyleProperty = { + type: STYLE_TYPE.DYNAMIC, + options: { + type: COLOR_MAP_TYPE.ORDINAL, + field, + fieldMetaOptions, + useCustomColorRamp: true, + } as ColorDynamicOptions, + } as ColorDynamicStylePropertyDescriptor; + expect(extractColorFromStyleProperty(colorStyleProperty, defaultColor)).toBe(defaultColor); + }); + + test('Should return middle color from custom color ramp (odd # of stops)', () => { + const colorStyleProperty = { + type: STYLE_TYPE.DYNAMIC, + options: { + type: COLOR_MAP_TYPE.ORDINAL, + field, + fieldMetaOptions, + useCustomColorRamp: true, + customColorRamp: [ + { stop: 0, color: blue }, + { stop: 5, color: green }, + { stop: 10, color: yellow }, + ], + } as ColorDynamicOptions, + } as ColorDynamicStylePropertyDescriptor; + expect(extractColorFromStyleProperty(colorStyleProperty, defaultColor)).toBe(green); + }); + + test('Should return middle color from custom color ramp (even # of stops)', () => { + const colorStyleProperty = { + type: STYLE_TYPE.DYNAMIC, + options: { + type: COLOR_MAP_TYPE.ORDINAL, + field, + fieldMetaOptions, + useCustomColorRamp: true, + customColorRamp: [ + { stop: 0, color: blue }, + { stop: 3, color: purple }, + { stop: 6, color: green }, + { stop: 10, color: yellow }, + ], + } as ColorDynamicOptions, + } as ColorDynamicStylePropertyDescriptor; + expect(extractColorFromStyleProperty(colorStyleProperty, defaultColor)).toBe(purple); + }); + }); + }); +}); diff --git a/x-pack/plugins/maps/public/classes/styles/vector/components/legend/extract_color_from_style_property.ts b/x-pack/plugins/maps/public/classes/styles/vector/components/legend/extract_color_from_style_property.ts new file mode 100644 index 0000000000000..71f77bc313191 --- /dev/null +++ b/x-pack/plugins/maps/public/classes/styles/vector/components/legend/extract_color_from_style_property.ts @@ -0,0 +1,63 @@ +/* + * 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. + */ + +// @ts-ignore +import { getColorRampCenterColor, getColorPalette } from '../../../color_utils'; +import { COLOR_MAP_TYPE, STYLE_TYPE } from '../../../../../../common/constants'; +import { + ColorDynamicOptions, + ColorStylePropertyDescriptor, +} from '../../../../../../common/descriptor_types'; + +export function extractColorFromStyleProperty( + colorStyleProperty: ColorStylePropertyDescriptor | undefined, + defaultColor: string +): string { + if (!colorStyleProperty) { + return defaultColor; + } + + if (colorStyleProperty.type === STYLE_TYPE.STATIC) { + return colorStyleProperty.options.color; + } + + const dynamicOptions: ColorDynamicOptions = colorStyleProperty.options; + + // Do not use dynamic color unless configuration is complete + if (!dynamicOptions.field || !dynamicOptions.field.name) { + return defaultColor; + } + + if (dynamicOptions.type === COLOR_MAP_TYPE.CATEGORICAL) { + if (dynamicOptions.useCustomColorPalette) { + return dynamicOptions.customColorPalette && dynamicOptions.customColorPalette.length + ? dynamicOptions.customColorPalette[0].color + : defaultColor; + } + + if (!dynamicOptions.colorCategory) { + return defaultColor; + } + + const palette = getColorPalette(dynamicOptions.colorCategory); + return palette[0]; + } else { + // return middle of gradient for dynamic style property + if (dynamicOptions.useCustomColorRamp) { + if (!dynamicOptions.customColorRamp || !dynamicOptions.customColorRamp.length) { + return defaultColor; + } + // favor the lowest color in even arrays + const middleIndex = Math.floor((dynamicOptions.customColorRamp.length - 1) / 2); + return dynamicOptions.customColorRamp[middleIndex].color; + } + + if (!dynamicOptions.color) { + return defaultColor; + } + return getColorRampCenterColor(dynamicOptions.color); + } +}