diff --git a/public/app/plugins/panel/geomap/editor/StyleEditor.tsx b/public/app/plugins/panel/geomap/editor/StyleEditor.tsx
index 56aba8718aa..2828d3a9bc8 100644
--- a/public/app/plugins/panel/geomap/editor/StyleEditor.tsx
+++ b/public/app/plugins/panel/geomap/editor/StyleEditor.tsx
@@ -31,7 +31,15 @@ import {
} from 'app/features/dimensions/editors';
import { ResourceFolderName, defaultTextConfig, MediaType } from 'app/features/dimensions/types';
-import { defaultStyleConfig, GeometryTypeId, StyleConfig, TextAlignment, TextBaseline } from '../style/types';
+import {
+ HorizontalAlign,
+ VerticalAlign,
+ defaultStyleConfig,
+ GeometryTypeId,
+ StyleConfig,
+ TextAlignment,
+ TextBaseline,
+} from '../style/types';
import { styleUsesText } from '../style/utils';
import { LayerContentInfo } from '../utils/getFeatures';
@@ -101,6 +109,14 @@ export const StyleEditor = (props: Props) => {
onChange({ ...value, textConfig: { ...value.textConfig, textBaseline: textBaseline } });
};
+ const onAlignHorizontalChange = (alignHorizontal: HorizontalAlign) => {
+ onChange({ ...value, symbolAlign: { ...value.symbolAlign, horizontal: alignHorizontal } });
+ };
+
+ const onAlignVerticalChange = (alignVertical: VerticalAlign) => {
+ onChange({ ...value, symbolAlign: { ...value.symbolAlign, vertical: alignVertical } });
+ };
+
const propertyOptions = useObservable(settings?.layerInfo ?? of());
const featuresHavePoints = propertyOptions?.geometryType === GeometryTypeId.Point;
const hasTextLabel = styleUsesText(value);
@@ -200,24 +216,48 @@ export const StyleEditor = (props: Props) => {
/>
{!settings?.hideSymbol && (
-
-
-
+ <>
+
+
+
+
+
+
+
+
+
+ >
)}
{
"fixed": "img/icons/marker/circle.svg",
"mode": "fixed",
},
+ "symbolAlign": {
+ "horizontal": "center",
+ "vertical": "center",
+ },
"textConfig": {
"fontSize": 12,
"offsetX": 0,
@@ -222,6 +226,10 @@ describe('geomap migrations', () => {
"fixed": "img/icons/marker/triangle.svg",
"mode": "fixed",
},
+ "symbolAlign": {
+ "horizontal": "center",
+ "vertical": "center",
+ },
"textConfig": {
"fontSize": 12,
"offsetX": 0,
diff --git a/public/app/plugins/panel/geomap/style/markers.ts b/public/app/plugins/panel/geomap/style/markers.ts
index 78da73af7af..293ed5d741b 100644
--- a/public/app/plugins/panel/geomap/style/markers.ts
+++ b/public/app/plugins/panel/geomap/style/markers.ts
@@ -6,6 +6,7 @@ import { config } from '@grafana/runtime';
import { getPublicOrAbsoluteUrl } from 'app/features/dimensions';
import { defaultStyleConfig, DEFAULT_SIZE, StyleConfigValues, StyleMaker } from './types';
+import { getDisplacement } from './utils';
interface SymbolMaker extends RegistryItem {
aliasIds: string[];
@@ -80,11 +81,13 @@ export const textMarker = (cfg: StyleConfigValues) => {
export const circleMarker = (cfg: StyleConfigValues) => {
const stroke = new Stroke({ color: cfg.color, width: cfg.lineWidth ?? 1 });
+ const radius = cfg.size ?? DEFAULT_SIZE;
return new Style({
image: new Circle({
stroke,
fill: getFillColor(cfg),
- radius: cfg.size ?? DEFAULT_SIZE,
+ radius,
+ displacement: getDisplacement(cfg.symbolAlign ?? defaultStyleConfig.symbolAlign, radius),
}),
text: textLabel(cfg),
stroke, // in case lines are sent to the markers layer
@@ -153,7 +156,9 @@ const makers: SymbolMaker[] = [
fill: getFillColor(cfg),
points: 4,
radius,
- rotation: (rotation * Math.PI) / 180 + Math.PI / 4,
+ angle: Math.PI / 4,
+ rotation: (rotation * Math.PI) / 180,
+ displacement: getDisplacement(cfg.symbolAlign ?? defaultStyleConfig.symbolAlign, radius),
}),
text: textLabel(cfg),
});
@@ -174,6 +179,7 @@ const makers: SymbolMaker[] = [
radius,
rotation: (rotation * Math.PI) / 180,
angle: 0,
+ displacement: getDisplacement(cfg.symbolAlign ?? defaultStyleConfig.symbolAlign, radius),
}),
text: textLabel(cfg),
});
@@ -195,6 +201,7 @@ const makers: SymbolMaker[] = [
radius2: radius * 0.4,
angle: 0,
rotation: (rotation * Math.PI) / 180,
+ displacement: getDisplacement(cfg.symbolAlign ?? defaultStyleConfig.symbolAlign, radius),
}),
text: textLabel(cfg),
});
@@ -215,6 +222,7 @@ const makers: SymbolMaker[] = [
radius2: 0,
angle: 0,
rotation: (rotation * Math.PI) / 180,
+ displacement: getDisplacement(cfg.symbolAlign ?? defaultStyleConfig.symbolAlign, radius),
}),
text: textLabel(cfg),
});
@@ -233,7 +241,9 @@ const makers: SymbolMaker[] = [
points: 4,
radius,
radius2: 0,
- rotation: (rotation * Math.PI) / 180 + Math.PI / 4,
+ angle: Math.PI / 4,
+ rotation: (rotation * Math.PI) / 180,
+ displacement: getDisplacement(cfg.symbolAlign ?? defaultStyleConfig.symbolAlign, radius),
}),
text: textLabel(cfg),
});
@@ -315,6 +325,7 @@ export async function getMarkerMaker(symbol?: string, hasTextLabel?: boolean): P
opacity: cfg.opacity ?? 1,
scale: (DEFAULT_SIZE + radius) / 100,
rotation: (rotation * Math.PI) / 180,
+ displacement: getDisplacement(cfg.symbolAlign ?? defaultStyleConfig.symbolAlign, radius / 2),
}),
text: !cfg?.text ? undefined : textLabel(cfg),
}),
diff --git a/public/app/plugins/panel/geomap/style/types.ts b/public/app/plugins/panel/geomap/style/types.ts
index 6f2a22ba732..cf8bc2c96ce 100644
--- a/public/app/plugins/panel/geomap/style/types.ts
+++ b/public/app/plugins/panel/geomap/style/types.ts
@@ -29,6 +29,7 @@ export interface StyleConfig {
// Used for points and dynamic text
size?: ScaleDimensionConfig;
symbol?: ResourceDimensionConfig;
+ symbolAlign?: SymbolAlign;
// Can show markers and text together!
text?: TextDimensionConfig;
@@ -50,6 +51,16 @@ export enum TextBaseline {
Middle = 'middle',
Bottom = 'bottom',
}
+export enum HorizontalAlign {
+ Left = 'left',
+ Center = 'center',
+ Right = 'right',
+}
+export enum VerticalAlign {
+ Top = 'top',
+ Center = 'center',
+ Bottom = 'bottom',
+}
export const defaultStyleConfig = Object.freeze({
size: {
@@ -65,6 +76,10 @@ export const defaultStyleConfig = Object.freeze({
mode: ResourceDimensionMode.Fixed,
fixed: 'img/icons/marker/circle.svg',
},
+ symbolAlign: {
+ horizontal: HorizontalAlign.Center,
+ vertical: VerticalAlign.Center,
+ },
textConfig: {
fontSize: 12,
textAlign: TextAlignment.Center,
@@ -80,6 +95,11 @@ export const defaultStyleConfig = Object.freeze({
},
});
+export interface SymbolAlign {
+ horizontal?: HorizontalAlign;
+ vertical?: VerticalAlign;
+}
+
/**
* Static options for text display. See:
* https://openlayers.org/en/latest/apidoc/module-ol_style_Text.html
@@ -99,6 +119,7 @@ export interface StyleConfigValues {
lineWidth?: number;
size?: number;
symbol?: string; // the point symbol
+ symbolAlign?: SymbolAlign;
rotation?: number;
text?: string;
diff --git a/public/app/plugins/panel/geomap/style/utils.test.ts b/public/app/plugins/panel/geomap/style/utils.test.ts
index 15546670b82..2b1e1f78e35 100644
--- a/public/app/plugins/panel/geomap/style/utils.test.ts
+++ b/public/app/plugins/panel/geomap/style/utils.test.ts
@@ -1,7 +1,7 @@
import { ResourceDimensionMode } from '@grafana/schema';
-import { StyleConfig } from './types';
-import { getStyleConfigState } from './utils';
+import { HorizontalAlign, VerticalAlign, StyleConfig, SymbolAlign } from './types';
+import { getDisplacement, getStyleConfigState } from './utils';
describe('style utils', () => {
it('should fill in default values', async () => {
@@ -41,6 +41,10 @@ describe('style utils', () => {
"opacity": 0.4,
"rotation": 0,
"size": 5,
+ "symbolAlign": {
+ "horizontal": "center",
+ "vertical": "center",
+ },
},
"config": null,
"fields": {
@@ -52,4 +56,22 @@ describe('style utils', () => {
}
`);
});
+ it('should return correct displacement array for top left', async () => {
+ const symbolAlign: SymbolAlign = { horizontal: HorizontalAlign.Left, vertical: VerticalAlign.Top };
+ const radius = 10;
+ const displacement = getDisplacement(symbolAlign, radius);
+ expect(displacement).toEqual([-10, 10]);
+ });
+ it('should return correct displacement array for bottom right', async () => {
+ const symbolAlign: SymbolAlign = { horizontal: HorizontalAlign.Right, vertical: VerticalAlign.Bottom };
+ const radius = 10;
+ const displacement = getDisplacement(symbolAlign, radius);
+ expect(displacement).toEqual([10, -10]);
+ });
+ it('should return correct displacement array for center center', async () => {
+ const symbolAlign: SymbolAlign = { horizontal: HorizontalAlign.Center, vertical: VerticalAlign.Center };
+ const radius = 10;
+ const displacement = getDisplacement(symbolAlign, radius);
+ expect(displacement).toEqual([0, 0]);
+ });
});
diff --git a/public/app/plugins/panel/geomap/style/utils.ts b/public/app/plugins/panel/geomap/style/utils.ts
index 9075b271339..8116fdead31 100644
--- a/public/app/plugins/panel/geomap/style/utils.ts
+++ b/public/app/plugins/panel/geomap/style/utils.ts
@@ -2,7 +2,15 @@ import { config } from '@grafana/runtime';
import { TextDimensionMode } from '@grafana/schema';
import { getMarkerMaker } from './markers';
-import { defaultStyleConfig, StyleConfig, StyleConfigFields, StyleConfigState } from './types';
+import {
+ HorizontalAlign,
+ VerticalAlign,
+ defaultStyleConfig,
+ StyleConfig,
+ StyleConfigFields,
+ StyleConfigState,
+ SymbolAlign,
+} from './types';
/** Indicate if the style wants to show text values */
export function styleUsesText(config: StyleConfig): boolean {
@@ -37,6 +45,7 @@ export async function getStyleConfigState(cfg?: StyleConfig): Promise