Geomap: support alternative marker shapes (#36916)
* set value default on init * shared location config * shared locaiton config * shared locaiton config * remove file * add alpha annotations * feature: icon picker * features: regular shapes picker * fix: set circle as default in select * use a registry for shapes * remove (unused) picker Co-authored-by: Ryan McKinley <ryantxu@gmail.com> Co-authored-by: An Le <anle@Ans-MBP.localdomain>
This commit is contained in:
co-authored by
Ryan McKinley
An Le
parent
50b7674b58
commit
71a9f7ce3c
@@ -58,7 +58,7 @@ export class GeomapPanel extends Component<Props> {
|
|||||||
this.map.updateSize();
|
this.map.updateSize();
|
||||||
}
|
}
|
||||||
|
|
||||||
// External configuraiton changed
|
// External configuration changed
|
||||||
let layersChanged = false;
|
let layersChanged = false;
|
||||||
if (this.props.options !== nextProps.options) {
|
if (this.props.options !== nextProps.options) {
|
||||||
layersChanged = this.optionsChanged(nextProps.options);
|
layersChanged = this.optionsChanged(nextProps.options);
|
||||||
@@ -86,7 +86,7 @@ export class GeomapPanel extends Component<Props> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (options.controls !== oldOptions.controls) {
|
if (options.controls !== oldOptions.controls) {
|
||||||
console.log('Crontrols changed');
|
console.log('Controls changed');
|
||||||
this.initControls(options.controls ?? { showZoom: true, showAttribution: true });
|
this.initControls(options.controls ?? { showZoom: true, showAttribution: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ export function getColorDimension(
|
|||||||
): DimensionSupplier<string> {
|
): DimensionSupplier<string> {
|
||||||
const field = findField(frame, config.field);
|
const field = findField(frame, config.field);
|
||||||
if (!field) {
|
if (!field) {
|
||||||
const v = config.fixed ?? 'grey';
|
const v = theme.visualization.getColorByName(config.fixed) ?? 'grey';
|
||||||
return {
|
return {
|
||||||
isAssumed: Boolean(config.field?.length) || !config.fixed,
|
isAssumed: Boolean(config.field?.length) || !config.fixed,
|
||||||
fixed: v,
|
fixed: v,
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ export const ColorDimensionEditor: FC<StandardEditorProps<ColorDimensionConfig,
|
|||||||
/>
|
/>
|
||||||
{isFixed && (
|
{isFixed && (
|
||||||
<div className={styles.picker}>
|
<div className={styles.picker}>
|
||||||
<ColorPicker color={value?.fixed ?? 'grey'} onChange={onColorChange} />
|
<ColorPicker color={value?.fixed ?? 'grey'} onChange={onColorChange} enableNamedColors={true} />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -49,7 +49,9 @@ export const publicServiceRegistry = new Registry<PublicServiceItem>(() => [
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
export interface ESRIXYZConfig extends XYZConfig {
|
export interface ESRIXYZConfig extends XYZConfig {
|
||||||
server: string;
|
config: {
|
||||||
|
server: string;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export const esriXYZTiles: MapLayerRegistryItem<ESRIXYZConfig> = {
|
export const esriXYZTiles: MapLayerRegistryItem<ESRIXYZConfig> = {
|
||||||
@@ -60,7 +62,7 @@ export const esriXYZTiles: MapLayerRegistryItem<ESRIXYZConfig> = {
|
|||||||
create: (map: Map, options: MapLayerOptions<ESRIXYZConfig>, theme: GrafanaTheme2) => ({
|
create: (map: Map, options: MapLayerOptions<ESRIXYZConfig>, theme: GrafanaTheme2) => ({
|
||||||
init: () => {
|
init: () => {
|
||||||
const cfg = { ...options.config };
|
const cfg = { ...options.config };
|
||||||
const svc = publicServiceRegistry.getIfExists(cfg.server ?? DEFAULT_SERVICE)!;
|
const svc = publicServiceRegistry.getIfExists(cfg.config?.server ?? DEFAULT_SERVICE)!;
|
||||||
if (svc.id !== CUSTOM_SERVICE) {
|
if (svc.id !== CUSTOM_SERVICE) {
|
||||||
const base = 'https://services.arcgisonline.com/ArcGIS/rest/services/';
|
const base = 'https://services.arcgisonline.com/ArcGIS/rest/services/';
|
||||||
cfg.url = `${base}${svc.slug}/MapServer/tile/{z}/{y}/{x}`;
|
cfg.url = `${base}${svc.slug}/MapServer/tile/{z}/{y}/{x}`;
|
||||||
@@ -87,7 +89,7 @@ export const esriXYZTiles: MapLayerRegistryItem<ESRIXYZConfig> = {
|
|||||||
settings: {
|
settings: {
|
||||||
placeholder: defaultXYZConfig.url,
|
placeholder: defaultXYZConfig.url,
|
||||||
},
|
},
|
||||||
showIf: (cfg) => cfg.server === CUSTOM_SERVICE,
|
showIf: (cfg) => cfg.config?.server === CUSTOM_SERVICE,
|
||||||
})
|
})
|
||||||
.addTextInput({
|
.addTextInput({
|
||||||
path: 'config.attribution',
|
path: 'config.attribution',
|
||||||
@@ -95,7 +97,7 @@ export const esriXYZTiles: MapLayerRegistryItem<ESRIXYZConfig> = {
|
|||||||
settings: {
|
settings: {
|
||||||
placeholder: defaultXYZConfig.attribution,
|
placeholder: defaultXYZConfig.attribution,
|
||||||
},
|
},
|
||||||
showIf: (cfg) => cfg.server === CUSTOM_SERVICE,
|
showIf: (cfg) => cfg.config?.server === CUSTOM_SERVICE,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import Map from 'ol/Map';
|
|||||||
import Feature from 'ol/Feature';
|
import Feature from 'ol/Feature';
|
||||||
import * as layer from 'ol/layer';
|
import * as layer from 'ol/layer';
|
||||||
import * as source from 'ol/source';
|
import * as source from 'ol/source';
|
||||||
import * as style from 'ol/style';
|
|
||||||
import tinycolor from 'tinycolor2';
|
import tinycolor from 'tinycolor2';
|
||||||
import { dataFrameToPoints, getLocationMatchers } from '../../utils/location';
|
import { dataFrameToPoints, getLocationMatchers } from '../../utils/location';
|
||||||
import { ColorDimensionConfig, ScaleDimensionConfig, } from '../../dims/types';
|
import { ColorDimensionConfig, ScaleDimensionConfig, } from '../../dims/types';
|
||||||
@@ -11,13 +11,14 @@ import { getScaledDimension, } from '../../dims/scale';
|
|||||||
import { getColorDimension, } from '../../dims/color';
|
import { getColorDimension, } from '../../dims/color';
|
||||||
import { ScaleDimensionEditor } from '../../dims/editors/ScaleDimensionEditor';
|
import { ScaleDimensionEditor } from '../../dims/editors/ScaleDimensionEditor';
|
||||||
import { ColorDimensionEditor } from '../../dims/editors/ColorDimensionEditor';
|
import { ColorDimensionEditor } from '../../dims/editors/ColorDimensionEditor';
|
||||||
|
import { markerMakers } from '../../utils/regularShapes';
|
||||||
|
|
||||||
// Configuration options for Circle overlays
|
// Configuration options for Circle overlays
|
||||||
export interface MarkersConfig {
|
export interface MarkersConfig {
|
||||||
size: ScaleDimensionConfig;
|
size: ScaleDimensionConfig;
|
||||||
color: ColorDimensionConfig;
|
color: ColorDimensionConfig;
|
||||||
fillOpacity: number;
|
fillOpacity: number;
|
||||||
|
shape?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const defaultOptions: MarkersConfig = {
|
const defaultOptions: MarkersConfig = {
|
||||||
@@ -27,9 +28,10 @@ const defaultOptions: MarkersConfig = {
|
|||||||
max: 15,
|
max: 15,
|
||||||
},
|
},
|
||||||
color: {
|
color: {
|
||||||
fixed: '#f00',
|
fixed: 'dark-green', // picked from theme
|
||||||
},
|
},
|
||||||
fillOpacity: 0.4,
|
fillOpacity: 0.4,
|
||||||
|
shape: 'circle',
|
||||||
};
|
};
|
||||||
|
|
||||||
export const MARKERS_LAYER_ID = "markers";
|
export const MARKERS_LAYER_ID = "markers";
|
||||||
@@ -65,6 +67,11 @@ export const markersLayer: MapLayerRegistryItem<MarkersConfig> = {
|
|||||||
...defaultOptions,
|
...defaultOptions,
|
||||||
...options?.config,
|
...options?.config,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let shape = markerMakers.getIfExists(config.shape);
|
||||||
|
if (!shape) {
|
||||||
|
shape = markerMakers.get('circle');
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
init: () => vectorLayer,
|
init: () => vectorLayer,
|
||||||
@@ -99,20 +106,7 @@ export const markersLayer: MapLayerRegistryItem<MarkersConfig> = {
|
|||||||
geometry: info.points[i],
|
geometry: info.points[i],
|
||||||
});
|
});
|
||||||
|
|
||||||
// Set the style of each feature dot
|
dot.setStyle(shape!.make(color, fillColor, radius));
|
||||||
dot.setStyle(new style.Style({
|
|
||||||
image: new style.Circle({
|
|
||||||
// Stroke determines the outline color of the circle
|
|
||||||
stroke: new style.Stroke({
|
|
||||||
color: color,
|
|
||||||
}),
|
|
||||||
// Fill determines the color to fill the whole circle
|
|
||||||
fill: new style.Fill({
|
|
||||||
color: tinycolor(fillColor).toString(),
|
|
||||||
}),
|
|
||||||
radius: radius,
|
|
||||||
})
|
|
||||||
}));
|
|
||||||
features.push(dot);
|
features.push(dot);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -150,16 +144,25 @@ export const markersLayer: MapLayerRegistryItem<MarkersConfig> = {
|
|||||||
max: 20,
|
max: 20,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
.addSelect({
|
||||||
|
path: 'config.shape',
|
||||||
|
name: 'Marker Shape',
|
||||||
|
settings: {
|
||||||
|
options: markerMakers.selectOptions().options,
|
||||||
|
},
|
||||||
|
defaultValue: 'circle',
|
||||||
|
})
|
||||||
.addSliderInput({
|
.addSliderInput({
|
||||||
path: 'config.fillOpacity',
|
path: 'config.fillOpacity',
|
||||||
name: 'Fill opacity',
|
name: 'Fill opacity',
|
||||||
defaultValue: defaultOptions.fillOpacity,
|
defaultValue: defaultOptions.fillOpacity,
|
||||||
settings: {
|
settings: {
|
||||||
min: 0,
|
min: 0,
|
||||||
max: 1,
|
max: 1,
|
||||||
step: 0.1,
|
step: 0.1,
|
||||||
},
|
},
|
||||||
});
|
showIf: (cfg) => (markerMakers.getIfExists((cfg as any).config?.shape)?.hasFill),
|
||||||
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
// fill in the default values
|
// fill in the default values
|
||||||
|
|||||||
@@ -0,0 +1,108 @@
|
|||||||
|
import { Fill, RegularShape, Stroke, Style, Circle } from 'ol/style';
|
||||||
|
import { Registry, RegistryItem } from '@grafana/data';
|
||||||
|
|
||||||
|
interface MarkerMaker extends RegistryItem {
|
||||||
|
make: (color: string, fillColor: string, radius: number) => Style;
|
||||||
|
hasFill: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const markerMakers = new Registry<MarkerMaker>(() => [
|
||||||
|
{
|
||||||
|
id: 'circle',
|
||||||
|
name: 'Circle',
|
||||||
|
hasFill: true,
|
||||||
|
make: (color: string, fillColor: string, radius: number) => {
|
||||||
|
return new Style({
|
||||||
|
image: new Circle({
|
||||||
|
stroke: new Stroke({ color: color }),
|
||||||
|
fill: new Fill({ color: fillColor }),
|
||||||
|
radius: radius,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'square',
|
||||||
|
name: 'Square',
|
||||||
|
hasFill: true,
|
||||||
|
make: (color: string, fillColor: string, radius: number) => {
|
||||||
|
return new Style({
|
||||||
|
image: new RegularShape({
|
||||||
|
fill: new Fill({ color: fillColor }),
|
||||||
|
stroke: new Stroke({ color: color, width: 1 }),
|
||||||
|
points: 4,
|
||||||
|
radius: radius,
|
||||||
|
angle: Math.PI / 4,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'triangle',
|
||||||
|
name: 'Triangle',
|
||||||
|
hasFill: true,
|
||||||
|
make: (color: string, fillColor: string, radius: number) => {
|
||||||
|
return new Style({
|
||||||
|
image: new RegularShape({
|
||||||
|
fill: new Fill({ color: fillColor }),
|
||||||
|
stroke: new Stroke({ color: color, width: 1 }),
|
||||||
|
points: 3,
|
||||||
|
radius: radius,
|
||||||
|
rotation: Math.PI / 4,
|
||||||
|
angle: 0,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'star',
|
||||||
|
name: 'Star',
|
||||||
|
hasFill: true,
|
||||||
|
make: (color: string, fillColor: string, radius: number) => {
|
||||||
|
return new Style({
|
||||||
|
image: new RegularShape({
|
||||||
|
fill: new Fill({ color: fillColor }),
|
||||||
|
stroke: new Stroke({ color: color, width: 1 }),
|
||||||
|
points: 5,
|
||||||
|
radius: radius,
|
||||||
|
radius2: radius * 0.4,
|
||||||
|
angle: 0,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'cross',
|
||||||
|
name: 'Cross',
|
||||||
|
hasFill: false,
|
||||||
|
make: (color: string, fillColor: string, radius: number) => {
|
||||||
|
return new Style({
|
||||||
|
image: new RegularShape({
|
||||||
|
fill: new Fill({ color: fillColor }),
|
||||||
|
stroke: new Stroke({ color: color, width: 1 }),
|
||||||
|
points: 4,
|
||||||
|
radius: radius,
|
||||||
|
radius2: 0,
|
||||||
|
angle: 0,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'x',
|
||||||
|
name: 'X',
|
||||||
|
hasFill: false,
|
||||||
|
make: (color: string, fillColor: string, radius: number) => {
|
||||||
|
return new Style({
|
||||||
|
image: new RegularShape({
|
||||||
|
fill: new Fill({ color: fillColor }),
|
||||||
|
stroke: new Stroke({ color: color, width: 1 }),
|
||||||
|
points: 4,
|
||||||
|
radius: radius,
|
||||||
|
radius2: 0,
|
||||||
|
angle: Math.PI / 4,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]);
|
||||||
Reference in New Issue
Block a user