Skip to content

Commit

Permalink
[Maps] Fix legend icon color for custom categorial palettes (#67141)
Browse files Browse the repository at this point in the history
Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
  • Loading branch information
nreese and elasticmachine authored May 26, 2020
1 parent c8b1fc5 commit c9c37f7
Show file tree
Hide file tree
Showing 5 changed files with 281 additions and 70 deletions.
8 changes: 4 additions & 4 deletions x-pack/plugins/maps/common/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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'];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
/* eslint-disable @typescript-eslint/consistent-type-definitions */

import {
COLOR_MAP_TYPE,
FIELD_ORIGIN,
LABEL_BORDER_SIZES,
SYMBOLIZE_AS_TYPES,
Expand Down Expand Up @@ -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;

Expand All @@ -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;
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -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);
});
});
});
});
Original file line number Diff line number Diff line change
@@ -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);
}
}

0 comments on commit c9c37f7

Please sign in to comment.