mirror of
https://github.com/grafana/grafana.git
synced 2025-02-03 20:21:01 -06:00
Geomap: simplify styles (#41842)
This commit is contained in:
parent
40d3072df2
commit
bf85ae44a2
@ -16,10 +16,10 @@ import { getScaledDimension, getColorDimension, getTextDimension } from 'app/fea
|
||||
import { ObservablePropsWrapper } from '../../components/ObservablePropsWrapper';
|
||||
import { MarkersLegend, MarkersLegendProps } from './MarkersLegend';
|
||||
import { ReplaySubject } from 'rxjs';
|
||||
import { FeaturesStylesBuilderConfig, getFeatures } from '../../utils/getFeatures';
|
||||
import { getMarkerMaker } from '../../style/markers';
|
||||
import { defaultStyleConfig, StyleConfig } from '../../style/types';
|
||||
import { getFeatures } from '../../utils/getFeatures';
|
||||
import { defaultStyleConfig, StyleConfig, StyleDimensions } from '../../style/types';
|
||||
import { StyleEditor } from './StyleEditor';
|
||||
import { getStyleConfigState } from '../../style/utils';
|
||||
|
||||
// Configuration options for Circle overlays
|
||||
export interface MarkersConfig {
|
||||
@ -73,9 +73,11 @@ export const markersLayer: MapLayerRegistryItem<MarkersConfig> = {
|
||||
legend = <ObservablePropsWrapper watch={legendProps} initialSubProps={{}} child={MarkersLegend} />;
|
||||
}
|
||||
|
||||
const style = config.style ?? defaultStyleConfig;
|
||||
const hasTextLabel = Boolean(style.text?.fixed || style.text?.field);
|
||||
const markerMaker = await getMarkerMaker(style.symbol?.fixed, hasTextLabel);
|
||||
// Set the default style
|
||||
const style = await getStyleConfigState(config.style);
|
||||
if (!style.fields) {
|
||||
vectorLayer.setStyle(style.maker(style.base));
|
||||
}
|
||||
|
||||
return {
|
||||
init: () => vectorLayer,
|
||||
@ -94,24 +96,21 @@ export const markersLayer: MapLayerRegistryItem<MarkersConfig> = {
|
||||
continue; // ???
|
||||
}
|
||||
|
||||
const colorDim = getColorDimension(frame, style.color ?? defaultStyleConfig.color, theme);
|
||||
const sizeDim = getScaledDimension(frame, style.size ?? defaultStyleConfig.size);
|
||||
let textDim = undefined;
|
||||
if (style?.text && (style.text.field || style.text.fixed)) {
|
||||
textDim = getTextDimension(frame, style.text);
|
||||
if (style.fields) {
|
||||
const dims: StyleDimensions = {};
|
||||
if (style.fields.color) {
|
||||
dims.color = getColorDimension(frame, style.config.color ?? defaultStyleConfig.color, theme);
|
||||
}
|
||||
if (style.fields.size) {
|
||||
dims.size = getScaledDimension(frame, style.config.size ?? defaultStyleConfig.size);
|
||||
}
|
||||
if (style.fields.text) {
|
||||
dims.text = getTextDimension(frame, style.config.text!);
|
||||
}
|
||||
style.dims = dims;
|
||||
}
|
||||
const opacity = style?.opacity ?? defaultStyleConfig.opacity;
|
||||
|
||||
const featureDimensionConfig: FeaturesStylesBuilderConfig = {
|
||||
colorDim: colorDim,
|
||||
sizeDim: sizeDim,
|
||||
textDim: textDim,
|
||||
textConfig: style?.textConfig,
|
||||
opacity: opacity,
|
||||
styleMaker: markerMaker,
|
||||
};
|
||||
|
||||
const frameFeatures = getFeatures(frame, info, featureDimensionConfig);
|
||||
const frameFeatures = getFeatures(frame, info, style);
|
||||
|
||||
if (frameFeatures) {
|
||||
features.push(...frameFeatures);
|
||||
@ -120,8 +119,8 @@ export const markersLayer: MapLayerRegistryItem<MarkersConfig> = {
|
||||
// Post updates to the legend component
|
||||
if (legend) {
|
||||
legendProps.next({
|
||||
color: colorDim,
|
||||
size: sizeDim,
|
||||
color: style.dims?.color,
|
||||
size: style.dims?.size,
|
||||
});
|
||||
}
|
||||
break; // Only the first frame for now!
|
||||
|
@ -1,5 +1,6 @@
|
||||
import {
|
||||
ColorDimensionConfig,
|
||||
DimensionSupplier,
|
||||
ResourceDimensionConfig,
|
||||
ResourceDimensionMode,
|
||||
ScaleDimensionConfig,
|
||||
@ -93,6 +94,28 @@ export interface StyleConfigValues {
|
||||
textConfig?: TextStyleConfig;
|
||||
}
|
||||
|
||||
/** When the style depends on a field */
|
||||
export interface StyleConfigFields {
|
||||
color?: string;
|
||||
size?: string;
|
||||
text?: string;
|
||||
}
|
||||
|
||||
export interface StyleDimensions {
|
||||
color?: DimensionSupplier<string>;
|
||||
size?: DimensionSupplier<number>;
|
||||
text?: DimensionSupplier<string>;
|
||||
}
|
||||
|
||||
export interface StyleConfigState {
|
||||
config: StyleConfig;
|
||||
hasText?: boolean;
|
||||
base: StyleConfigValues;
|
||||
fields?: StyleConfigFields;
|
||||
dims?: StyleDimensions;
|
||||
maker: StyleMaker;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given values create a style
|
||||
*/
|
||||
|
54
public/app/plugins/panel/geomap/style/utils.test.ts
Normal file
54
public/app/plugins/panel/geomap/style/utils.test.ts
Normal file
@ -0,0 +1,54 @@
|
||||
import { ResourceDimensionMode } from 'app/features/dimensions';
|
||||
import { StyleConfig } from './types';
|
||||
import { getStyleConfigState } from './utils';
|
||||
|
||||
describe('style utils', () => {
|
||||
it('should fill in default values', async () => {
|
||||
const cfg: StyleConfig = {
|
||||
color: {
|
||||
field: 'Price',
|
||||
fixed: 'dark-green',
|
||||
},
|
||||
opacity: 0.4,
|
||||
size: {
|
||||
field: 'Count',
|
||||
fixed: 5,
|
||||
max: 15,
|
||||
min: 2,
|
||||
},
|
||||
symbol: {
|
||||
fixed: 'img/icons/marker/star.svg',
|
||||
mode: ResourceDimensionMode.Fixed, // 'fixed',
|
||||
},
|
||||
textConfig: {
|
||||
fontSize: 12,
|
||||
offsetX: 0,
|
||||
offsetY: 0,
|
||||
// textAlign: 'center',
|
||||
// textBaseline: 'middle',
|
||||
},
|
||||
};
|
||||
|
||||
const state = await getStyleConfigState(cfg);
|
||||
state.config = null as any; // not interesting in the snapshot
|
||||
expect(state.hasText).toBe(false);
|
||||
expect(state).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"base": Object {
|
||||
"color": "#37872D",
|
||||
"lineWidth": 1,
|
||||
"opacity": 0.4,
|
||||
"rotation": 0,
|
||||
"size": 5,
|
||||
},
|
||||
"config": null,
|
||||
"fields": Object {
|
||||
"color": "Price",
|
||||
"size": "Count",
|
||||
},
|
||||
"hasText": false,
|
||||
"maker": [Function],
|
||||
}
|
||||
`);
|
||||
});
|
||||
});
|
@ -1,18 +1,64 @@
|
||||
import { StyleConfig } from './types';
|
||||
import { config } from '@grafana/runtime';
|
||||
import { TextDimensionMode } from 'app/features/dimensions';
|
||||
import { getMarkerMaker } from './markers';
|
||||
import { defaultStyleConfig, StyleConfig, StyleConfigFields, StyleConfigState } from './types';
|
||||
|
||||
/** Indicate if the style wants to show text values */
|
||||
export function styleUsesText(config: StyleConfig): boolean {
|
||||
const { text } = config;
|
||||
if (!text) {
|
||||
return false;
|
||||
}
|
||||
if (text.mode === TextDimensionMode.Fixed && text.fixed?.length) {
|
||||
return true;
|
||||
}
|
||||
if (text.mode === TextDimensionMode.Field && text.field?.length) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Return a distinct list of fields used to dynamically change the style */
|
||||
export function getDependantFields(config: StyleConfig): Set<string> | undefined {
|
||||
const fields = new Set<string>();
|
||||
export async function getStyleConfigState(cfg?: StyleConfig): Promise<StyleConfigState> {
|
||||
if (!cfg) {
|
||||
cfg = defaultStyleConfig;
|
||||
}
|
||||
const hasText = styleUsesText(cfg);
|
||||
const fields: StyleConfigFields = {};
|
||||
const maker = await getMarkerMaker(cfg.symbol?.fixed, hasText);
|
||||
const state: StyleConfigState = {
|
||||
config: cfg, // raw values
|
||||
hasText,
|
||||
fields,
|
||||
base: {
|
||||
color: config.theme2.visualization.getColorByName(cfg.color?.fixed ?? defaultStyleConfig.color.fixed),
|
||||
opacity: cfg.opacity ?? defaultStyleConfig.opacity,
|
||||
lineWidth: cfg.lineWidth ?? 1,
|
||||
size: cfg.size?.fixed ?? defaultStyleConfig.size.fixed,
|
||||
rotation: 0, // dynamic will follow path
|
||||
},
|
||||
maker,
|
||||
};
|
||||
|
||||
if (config.color?.field) {
|
||||
fields.add(config.color.field);
|
||||
if (cfg.color?.field?.length) {
|
||||
fields.color = cfg.color.field;
|
||||
}
|
||||
if (config.size?.field) {
|
||||
fields.add(config.size.field);
|
||||
}
|
||||
if (config.text?.field) {
|
||||
fields.add(config.text.field);
|
||||
if (cfg.size?.field?.length) {
|
||||
fields.size = cfg.size.field;
|
||||
}
|
||||
|
||||
return fields;
|
||||
if (hasText) {
|
||||
state.base.text = cfg.text?.fixed;
|
||||
state.base.textConfig = cfg.textConfig ?? defaultStyleConfig.textConfig;
|
||||
|
||||
if (cfg.text?.field?.length) {
|
||||
fields.text = cfg.text.field;
|
||||
}
|
||||
}
|
||||
|
||||
// Clear the fields if possible
|
||||
if (!Object.keys(fields).length) {
|
||||
state.fields = undefined;
|
||||
}
|
||||
return state;
|
||||
}
|
||||
|
@ -1,41 +1,20 @@
|
||||
import { DataFrame } from '@grafana/data';
|
||||
import { DimensionSupplier } from 'app/features/dimensions';
|
||||
import { Feature } from 'ol';
|
||||
import { Point } from 'ol/geom';
|
||||
import { StyleMaker, TextStyleConfig } from '../style/types';
|
||||
import { StyleConfigState } from '../style/types';
|
||||
import { LocationInfo } from './location';
|
||||
|
||||
export interface FeaturesStylesBuilderConfig {
|
||||
colorDim: DimensionSupplier<string>;
|
||||
sizeDim: DimensionSupplier<number>;
|
||||
opacity: number;
|
||||
styleMaker: StyleMaker;
|
||||
textDim?: DimensionSupplier<string>;
|
||||
textConfig?: TextStyleConfig;
|
||||
}
|
||||
|
||||
export const getFeatures = (
|
||||
frame: DataFrame,
|
||||
info: LocationInfo,
|
||||
config: FeaturesStylesBuilderConfig
|
||||
style: StyleConfigState
|
||||
): Array<Feature<Point>> | undefined => {
|
||||
const features: Array<Feature<Point>> = [];
|
||||
const opacity = config.opacity;
|
||||
const { dims } = style;
|
||||
const values = { ...style.base };
|
||||
|
||||
// Map each data value into new points
|
||||
for (let i = 0; i < frame.length; i++) {
|
||||
// Get the color for the feature based on color scheme
|
||||
const color = config.colorDim.get(i);
|
||||
|
||||
// Get the size for the feature based on size dimension
|
||||
const size = config.sizeDim.get(i);
|
||||
|
||||
// Get the text for the feature based on text dimension
|
||||
const text = config?.textDim ? config?.textDim.get(i) : undefined;
|
||||
|
||||
// Get the textConfig
|
||||
const textConfig = config?.textConfig;
|
||||
|
||||
// Create a new Feature for each point returned from dataFrameToPoints
|
||||
const dot = new Feature(info.points[i]);
|
||||
dot.setProperties({
|
||||
@ -43,7 +22,20 @@ export const getFeatures = (
|
||||
rowIndex: i,
|
||||
});
|
||||
|
||||
dot.setStyle(config.styleMaker({ color, size, text, opacity, textConfig }));
|
||||
// Update values used in dynamic styles
|
||||
if (dims) {
|
||||
if (dims.color) {
|
||||
values.color = dims.color.get(i);
|
||||
}
|
||||
if (dims.size) {
|
||||
values.size = dims.size.get(i);
|
||||
}
|
||||
if (dims.text) {
|
||||
values.text = dims.text.get(i);
|
||||
}
|
||||
|
||||
dot.setStyle(style.maker(values));
|
||||
}
|
||||
features.push(dot);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user