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:
An
2021-07-20 21:09:08 -04:00
committed by GitHub
co-authored by Ryan McKinley An Le
parent 50b7674b58
commit 71a9f7ce3c
6 changed files with 147 additions and 34 deletions
@@ -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,
}),
});
},
},
]);