[Maps] convert HeatmapLayer dependencies to TS (#66823)
* [Maps] convert HeatmapLayer and dependencies to TS * heatmap_style_editor snapshots * eslint * fix merge problems * eslint cleanup * revert rename of getOrdinalMbColorRampStops * eslint * tslint Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
This commit is contained in:
parent
17573f1a0b
commit
57345e092e
13 changed files with 117 additions and 51 deletions
|
@ -27,7 +27,6 @@ import {
|
|||
VECTOR_STYLES,
|
||||
STYLE_TYPE,
|
||||
} from '../../../../common/constants';
|
||||
// @ts-ignore
|
||||
import { COLOR_GRADIENTS } from '../../styles/color_utils';
|
||||
|
||||
export const clustersLayerWizardConfig: LayerWizard = {
|
||||
|
|
|
@ -17,7 +17,6 @@ import {
|
|||
VECTOR_STYLES,
|
||||
STYLE_TYPE,
|
||||
} from '../../../../common/constants';
|
||||
// @ts-ignore
|
||||
import { COLOR_GRADIENTS } from '../../styles/color_utils';
|
||||
// @ts-ignore
|
||||
import { CreateSourceEditor } from './create_source_editor';
|
||||
|
|
|
@ -7,73 +7,85 @@
|
|||
import React from 'react';
|
||||
import tinycolor from 'tinycolor2';
|
||||
import chroma from 'chroma-js';
|
||||
// @ts-ignore
|
||||
import { euiPaletteColorBlind } from '@elastic/eui/lib/services';
|
||||
import { ColorGradient } from './components/color_gradient';
|
||||
import { vislibColorMaps } from '../../../../../../src/plugins/charts/public';
|
||||
import { RawColorSchema, vislibColorMaps } from '../../../../../../src/plugins/charts/public';
|
||||
|
||||
export const GRADIENT_INTERVALS = 8;
|
||||
|
||||
export const DEFAULT_FILL_COLORS = euiPaletteColorBlind();
|
||||
export const DEFAULT_LINE_COLORS = [
|
||||
...DEFAULT_FILL_COLORS.map((color) => tinycolor(color).darken().toHexString()),
|
||||
export const DEFAULT_FILL_COLORS: string[] = euiPaletteColorBlind();
|
||||
export const DEFAULT_LINE_COLORS: string[] = [
|
||||
...DEFAULT_FILL_COLORS.map((color: string) => tinycolor(color).darken().toHexString()),
|
||||
// Explicitly add black & white as border color options
|
||||
'#000',
|
||||
'#FFF',
|
||||
];
|
||||
|
||||
function getLegendColors(colorRamp, numLegendColors = 4) {
|
||||
function getRGBColors(colorRamp: Array<[number, number[]]>, numLegendColors: number = 4): string[] {
|
||||
const colors = [];
|
||||
colors[0] = getColor(colorRamp, 0);
|
||||
colors[0] = getRGBColor(colorRamp, 0);
|
||||
for (let i = 1; i < numLegendColors - 1; i++) {
|
||||
colors[i] = getColor(colorRamp, Math.floor((colorRamp.length * i) / numLegendColors));
|
||||
colors[i] = getRGBColor(colorRamp, Math.floor((colorRamp.length * i) / numLegendColors));
|
||||
}
|
||||
colors[numLegendColors - 1] = getColor(colorRamp, colorRamp.length - 1);
|
||||
colors[numLegendColors - 1] = getRGBColor(colorRamp, colorRamp.length - 1);
|
||||
return colors;
|
||||
}
|
||||
|
||||
function getColor(colorRamp, i) {
|
||||
const color = colorRamp[i][1];
|
||||
const red = Math.floor(color[0] * 255);
|
||||
const green = Math.floor(color[1] * 255);
|
||||
const blue = Math.floor(color[2] * 255);
|
||||
function getRGBColor(colorRamp: Array<[number, number[]]>, i: number): string {
|
||||
const rgbArray = colorRamp[i][1];
|
||||
const red = Math.floor(rgbArray[0] * 255);
|
||||
const green = Math.floor(rgbArray[1] * 255);
|
||||
const blue = Math.floor(rgbArray[2] * 255);
|
||||
return `rgb(${red},${green},${blue})`;
|
||||
}
|
||||
|
||||
function getColorRamp(colorRampName) {
|
||||
const colorRamp = vislibColorMaps[colorRampName];
|
||||
if (!colorRamp) {
|
||||
function getColorSchema(colorRampName: string): RawColorSchema {
|
||||
const colorSchema = vislibColorMaps[colorRampName];
|
||||
if (!colorSchema) {
|
||||
throw new Error(
|
||||
`${colorRampName} not found. Expected one of following values: ${Object.keys(
|
||||
vislibColorMaps
|
||||
)}`
|
||||
);
|
||||
}
|
||||
return colorRamp;
|
||||
return colorSchema;
|
||||
}
|
||||
|
||||
export function getRGBColorRangeStrings(colorRampName, numberColors = GRADIENT_INTERVALS) {
|
||||
const colorRamp = getColorRamp(colorRampName);
|
||||
return getLegendColors(colorRamp.value, numberColors);
|
||||
export function getRGBColorRangeStrings(
|
||||
colorRampName: string,
|
||||
numberColors: number = GRADIENT_INTERVALS
|
||||
): string[] {
|
||||
const colorSchema = getColorSchema(colorRampName);
|
||||
return getRGBColors(colorSchema.value, numberColors);
|
||||
}
|
||||
|
||||
export function getHexColorRangeStrings(colorRampName, numberColors = GRADIENT_INTERVALS) {
|
||||
export function getHexColorRangeStrings(
|
||||
colorRampName: string,
|
||||
numberColors: number = GRADIENT_INTERVALS
|
||||
): string[] {
|
||||
return getRGBColorRangeStrings(colorRampName, numberColors).map((rgbColor) =>
|
||||
chroma(rgbColor).hex()
|
||||
);
|
||||
}
|
||||
|
||||
export function getColorRampCenterColor(colorRampName) {
|
||||
export function getColorRampCenterColor(colorRampName: string): string | null {
|
||||
if (!colorRampName) {
|
||||
return null;
|
||||
}
|
||||
const colorRamp = getColorRamp(colorRampName);
|
||||
const centerIndex = Math.floor(colorRamp.value.length / 2);
|
||||
return getColor(colorRamp.value, centerIndex);
|
||||
const colorSchema = getColorSchema(colorRampName);
|
||||
const centerIndex = Math.floor(colorSchema.value.length / 2);
|
||||
return getRGBColor(colorSchema.value, centerIndex);
|
||||
}
|
||||
|
||||
// Returns an array of color stops
|
||||
// [ stop_input_1: number, stop_output_1: color, stop_input_n: number, stop_output_n: color ]
|
||||
export function getOrdinalMbColorRampStops(colorRampName, min, max, numberColors) {
|
||||
export function getOrdinalMbColorRampStops(
|
||||
colorRampName: string,
|
||||
min: number,
|
||||
max: number,
|
||||
numberColors: number
|
||||
): Array<number | string> | null {
|
||||
if (!colorRampName) {
|
||||
return null;
|
||||
}
|
||||
|
@ -84,15 +96,18 @@ export function getOrdinalMbColorRampStops(colorRampName, min, max, numberColors
|
|||
|
||||
const hexColors = getHexColorRangeStrings(colorRampName, numberColors);
|
||||
if (max === min) {
|
||||
//just return single stop value
|
||||
// just return single stop value
|
||||
return [max, hexColors[hexColors.length - 1]];
|
||||
}
|
||||
|
||||
const delta = max - min;
|
||||
return hexColors.reduce((accu, stopColor, idx, srcArr) => {
|
||||
const stopNumber = min + (delta * idx) / srcArr.length;
|
||||
return [...accu, stopNumber, stopColor];
|
||||
}, []);
|
||||
return hexColors.reduce(
|
||||
(accu: Array<number | string>, stopColor: string, idx: number, srcArr: string[]) => {
|
||||
const stopNumber = min + (delta * idx) / srcArr.length;
|
||||
return [...accu, stopNumber, stopColor];
|
||||
},
|
||||
[]
|
||||
);
|
||||
}
|
||||
|
||||
export const COLOR_GRADIENTS = Object.keys(vislibColorMaps).map((colorRampName) => ({
|
||||
|
@ -102,7 +117,7 @@ export const COLOR_GRADIENTS = Object.keys(vislibColorMaps).map((colorRampName)
|
|||
|
||||
export const COLOR_RAMP_NAMES = Object.keys(vislibColorMaps);
|
||||
|
||||
export function getLinearGradient(colorStrings) {
|
||||
export function getLinearGradient(colorStrings: string[]): string {
|
||||
const intervals = colorStrings.length;
|
||||
let linearGradient = `linear-gradient(to right, ${colorStrings[0]} 0%,`;
|
||||
for (let i = 1; i < intervals - 1; i++) {
|
||||
|
@ -112,7 +127,12 @@ export function getLinearGradient(colorStrings) {
|
|||
return `${linearGradient} ${colorStrings[colorStrings.length - 1]} 100%)`;
|
||||
}
|
||||
|
||||
const COLOR_PALETTES_CONFIGS = [
|
||||
export interface ColorPalette {
|
||||
id: string;
|
||||
colors: string[];
|
||||
}
|
||||
|
||||
const COLOR_PALETTES_CONFIGS: ColorPalette[] = [
|
||||
{
|
||||
id: 'palette_0',
|
||||
colors: euiPaletteColorBlind(),
|
||||
|
@ -127,14 +147,14 @@ const COLOR_PALETTES_CONFIGS = [
|
|||
},
|
||||
];
|
||||
|
||||
export function getColorPalette(paletteId) {
|
||||
const palette = COLOR_PALETTES_CONFIGS.find((palette) => palette.id === paletteId);
|
||||
export function getColorPalette(paletteId: string): string[] | null {
|
||||
const palette = COLOR_PALETTES_CONFIGS.find(({ id }: ColorPalette) => id === paletteId);
|
||||
return palette ? palette.colors : null;
|
||||
}
|
||||
|
||||
export const COLOR_PALETTES = COLOR_PALETTES_CONFIGS.map((palette) => {
|
||||
const paletteDisplay = palette.colors.map((color) => {
|
||||
const style = {
|
||||
const style: React.CSSProperties = {
|
||||
backgroundColor: color,
|
||||
width: `${100 / palette.colors.length}%`,
|
||||
position: 'relative',
|
|
@ -11,17 +11,20 @@ import {
|
|||
getRGBColorRangeStrings,
|
||||
getLinearGradient,
|
||||
} from '../color_utils';
|
||||
import classNames from 'classnames';
|
||||
|
||||
export const ColorGradient = ({ colorRamp, colorRampName, className }) => {
|
||||
interface Props {
|
||||
colorRamp?: string[];
|
||||
colorRampName?: string;
|
||||
}
|
||||
|
||||
export const ColorGradient = ({ colorRamp, colorRampName }: Props) => {
|
||||
if (!colorRamp && (!colorRampName || !COLOR_RAMP_NAMES.includes(colorRampName))) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const classes = classNames('mapColorGradient', className);
|
||||
const rgbColorStrings = colorRampName
|
||||
? getRGBColorRangeStrings(colorRampName, GRADIENT_INTERVALS)
|
||||
: colorRamp;
|
||||
: colorRamp!;
|
||||
const background = getLinearGradient(rgbColorStrings);
|
||||
return <div className={classes} style={{ background }} />;
|
||||
return <div className="mapColorGradient" style={{ background }} />;
|
||||
};
|
|
@ -15,8 +15,13 @@ import {
|
|||
HEATMAP_COLOR_RAMP_LABEL,
|
||||
} from './heatmap_constants';
|
||||
|
||||
export function HeatmapStyleEditor({ colorRampName, onHeatmapColorChange }) {
|
||||
const onColorRampChange = (selectedColorRampName) => {
|
||||
interface Props {
|
||||
colorRampName: string;
|
||||
onHeatmapColorChange: ({ colorRampName }: { colorRampName: string }) => void;
|
||||
}
|
||||
|
||||
export function HeatmapStyleEditor({ colorRampName, onHeatmapColorChange }: Props) {
|
||||
const onColorRampChange = (selectedColorRampName: string) => {
|
||||
onHeatmapColorChange({
|
||||
colorRampName: selectedColorRampName,
|
||||
});
|
|
@ -91,6 +91,7 @@ export class HeatmapStyle extends AbstractStyle {
|
|||
MAX_RANGE,
|
||||
GRADIENT_INTERVALS
|
||||
);
|
||||
// TODO handle null
|
||||
mbMap.setPaintProperty(layerId, 'heatmap-color', [
|
||||
'interpolate',
|
||||
['linear'],
|
||||
|
|
|
@ -43,7 +43,7 @@ export function extractColorFromStyleProperty(
|
|||
}
|
||||
|
||||
const palette = getColorPalette(dynamicOptions.colorCategory);
|
||||
return palette[0];
|
||||
return palette ? palette[0] : defaultColor;
|
||||
} else {
|
||||
// return middle of gradient for dynamic style property
|
||||
if (dynamicOptions.useCustomColorRamp) {
|
||||
|
@ -58,6 +58,7 @@ export function extractColorFromStyleProperty(
|
|||
if (!dynamicOptions.color) {
|
||||
return defaultColor;
|
||||
}
|
||||
return getColorRampCenterColor(dynamicOptions.color);
|
||||
const centerColor = getColorRampCenterColor(dynamicOptions.color);
|
||||
return centerColor ? centerColor : defaultColor;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
jest.mock('../../../kibana_services', () => {
|
||||
const mockUiSettings = {
|
||||
get: () => {
|
||||
return undefined;
|
||||
},
|
||||
};
|
||||
return {
|
||||
getUiSettings: () => {
|
||||
return mockUiSettings;
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
import { VECTOR_STYLES } from '../../../../common/constants';
|
||||
import { getDefaultStaticProperties } from './vector_style_defaults';
|
||||
|
||||
describe('getDefaultStaticProperties', () => {
|
||||
test('Should use first color in DEFAULT_*_COLORS when no colors are used on the map', () => {
|
||||
const styleProperties = getDefaultStaticProperties([]);
|
||||
expect(styleProperties[VECTOR_STYLES.FILL_COLOR]!.options.color).toBe('#54B399');
|
||||
expect(styleProperties[VECTOR_STYLES.LINE_COLOR]!.options.color).toBe('#41937c');
|
||||
});
|
||||
|
||||
test('Should next color in DEFAULT_*_COLORS when colors are used on the map', () => {
|
||||
const styleProperties = getDefaultStaticProperties(['#54B399']);
|
||||
expect(styleProperties[VECTOR_STYLES.FILL_COLOR]!.options.color).toBe('#6092C0');
|
||||
expect(styleProperties[VECTOR_STYLES.LINE_COLOR]!.options.color).toBe('#4379aa');
|
||||
});
|
||||
});
|
|
@ -16,7 +16,6 @@ import {
|
|||
COLOR_PALETTES,
|
||||
DEFAULT_FILL_COLORS,
|
||||
DEFAULT_LINE_COLORS,
|
||||
// @ts-ignore
|
||||
} from '../color_utils';
|
||||
import { VectorStylePropertiesDescriptor } from '../../../../common/descriptor_types';
|
||||
// @ts-ignore
|
||||
|
@ -58,9 +57,13 @@ export function getDefaultProperties(mapColors: string[] = []): VectorStylePrope
|
|||
export function getDefaultStaticProperties(
|
||||
mapColors: string[] = []
|
||||
): VectorStylePropertiesDescriptor {
|
||||
// Colors must be state-aware to reduce unnecessary incrementation
|
||||
const lastColor = mapColors.pop();
|
||||
const nextColorIndex = (DEFAULT_FILL_COLORS.indexOf(lastColor) + 1) % DEFAULT_FILL_COLORS.length;
|
||||
let nextColorIndex = 0;
|
||||
if (mapColors.length) {
|
||||
const lastColor = mapColors[mapColors.length - 1];
|
||||
if (DEFAULT_FILL_COLORS.includes(lastColor)) {
|
||||
nextColorIndex = (DEFAULT_FILL_COLORS.indexOf(lastColor) + 1) % DEFAULT_FILL_COLORS.length;
|
||||
}
|
||||
}
|
||||
const nextFillColor = DEFAULT_FILL_COLORS[nextColorIndex];
|
||||
const nextLineColor = DEFAULT_LINE_COLORS[nextColorIndex];
|
||||
|
||||
|
|
Loading…
Reference in a new issue