261 lines
7.3 KiB
TypeScript
261 lines
7.3 KiB
TypeScript
import Feature from 'ol/Feature';
|
|
import Map from 'ol/Map';
|
|
import { Coordinate } from 'ol/coordinate';
|
|
import { MultiLineString } from 'ol/geom';
|
|
import Point from 'ol/geom/Point';
|
|
import { Group as LayerGroup } from 'ol/layer';
|
|
import VectorImage from 'ol/layer/VectorImage';
|
|
import { fromLonLat } from 'ol/proj';
|
|
import VectorSource from 'ol/source/Vector';
|
|
import { Fill, Stroke, Style, Circle } from 'ol/style';
|
|
import DayNight from 'ol-ext/source/DayNight';
|
|
import { Subscription } from 'rxjs';
|
|
|
|
import {
|
|
MapLayerRegistryItem,
|
|
MapLayerOptions,
|
|
PanelData,
|
|
GrafanaTheme2,
|
|
EventBus,
|
|
DataHoverEvent,
|
|
DataHoverClearEvent,
|
|
} from '@grafana/data';
|
|
|
|
export enum ShowTime {
|
|
From = 'from',
|
|
To = 'to',
|
|
}
|
|
|
|
// Configuration options for Circle overlays
|
|
export interface DayNightConfig {
|
|
show: ShowTime;
|
|
sun: boolean;
|
|
nightColor: string;
|
|
}
|
|
|
|
const defaultConfig: DayNightConfig = {
|
|
show: ShowTime.To,
|
|
sun: false,
|
|
nightColor: '#a7a6ba4D',
|
|
};
|
|
|
|
export const DAY_NIGHT_LAYER_ID = 'dayNight';
|
|
|
|
// Used by default when nothing is configured
|
|
export const defaultDayNightConfig: MapLayerOptions<DayNightConfig> = {
|
|
type: DAY_NIGHT_LAYER_ID,
|
|
name: '', // will get replaced
|
|
config: defaultConfig,
|
|
tooltip: true,
|
|
};
|
|
|
|
/**
|
|
* Map layer configuration for circle overlay
|
|
*/
|
|
export const dayNightLayer: MapLayerRegistryItem<DayNightConfig> = {
|
|
id: DAY_NIGHT_LAYER_ID,
|
|
name: 'Night / Day',
|
|
description: 'Show day and night regions',
|
|
isBaseMap: false,
|
|
|
|
/**
|
|
* Function that configures transformation and returns a transformer
|
|
* @param map
|
|
* @param options
|
|
* @param theme
|
|
*/
|
|
create: async (map: Map, options: MapLayerOptions<DayNightConfig>, eventBus: EventBus, theme: GrafanaTheme2) => {
|
|
// Assert default values
|
|
const config = {
|
|
...defaultConfig,
|
|
...options?.config,
|
|
};
|
|
|
|
// DayNight source
|
|
const source = new DayNight({});
|
|
const sourceMethods = Object.getPrototypeOf(source);
|
|
const sourceLine = new DayNight({});
|
|
const sourceLineMethods = Object.getPrototypeOf(sourceLine);
|
|
|
|
// Night polygon
|
|
const vectorLayer = new VectorImage({
|
|
source,
|
|
style: new Style({
|
|
fill: new Fill({
|
|
color: theme.visualization.getColorByName(config.nightColor),
|
|
}),
|
|
}),
|
|
});
|
|
|
|
// Night line (for crosshair sharing)
|
|
const nightLineLayer = new VectorImage({
|
|
source: new VectorSource({
|
|
features: [],
|
|
}),
|
|
style: new Style({
|
|
stroke: new Stroke({
|
|
color: '#607D8B',
|
|
width: 1.5,
|
|
lineDash: [2, 3],
|
|
}),
|
|
}),
|
|
});
|
|
|
|
// Sun circle
|
|
const sunFeature = new Feature({
|
|
geometry: new Point([]),
|
|
});
|
|
|
|
const sunLayer = new VectorImage({
|
|
source: new VectorSource({
|
|
features: [sunFeature],
|
|
}),
|
|
style: new Style({
|
|
image: new Circle({
|
|
radius: 13,
|
|
fill: new Fill({ color: 'rgb(253,184,19)' }),
|
|
}),
|
|
}),
|
|
});
|
|
|
|
// Sun line (for crosshair sharing)
|
|
const sunLineFeature = new Feature({
|
|
geometry: new Point([]),
|
|
});
|
|
|
|
const sunLineStyle = new Style({
|
|
image: new Circle({
|
|
radius: 13,
|
|
stroke: new Stroke({
|
|
color: 'rgb(253,184,19)',
|
|
width: 1.5,
|
|
}),
|
|
}),
|
|
});
|
|
|
|
const sunLineStyleDash = new Style({
|
|
image: new Circle({
|
|
radius: 15,
|
|
stroke: new Stroke({
|
|
color: '#607D8B',
|
|
width: 1.5,
|
|
lineDash: [2, 3],
|
|
}),
|
|
}),
|
|
});
|
|
|
|
const sunLineLayer = new VectorImage({
|
|
source: new VectorSource({
|
|
features: [sunLineFeature],
|
|
}),
|
|
style: [sunLineStyleDash, sunLineStyle],
|
|
});
|
|
|
|
// Build group of layers
|
|
// TODO: add blended night region to "connect" current night region to lines
|
|
const layer = new LayerGroup({
|
|
layers: config.sun ? [vectorLayer, sunLayer, sunLineLayer, nightLineLayer] : [vectorLayer, nightLineLayer],
|
|
});
|
|
|
|
// Crosshair sharing subscriptions
|
|
const subscriptions = new Subscription();
|
|
|
|
if (false) {
|
|
subscriptions.add(
|
|
eventBus.subscribe(DataHoverEvent, (event) => {
|
|
const time = event.payload?.point?.time;
|
|
if (time) {
|
|
const lineTime = new Date(time);
|
|
const nightLinePoints = sourceLine.getCoordinates(lineTime.toString(), 'line');
|
|
nightLineLayer.getSource()?.clear();
|
|
const lineStringArray: Coordinate[][] = [];
|
|
for (let l = 0; l < nightLinePoints.length - 1; l++) {
|
|
const x1: number = Object.values(nightLinePoints[l])[0];
|
|
const y1: number = Object.values(nightLinePoints[l])[1];
|
|
const x2: number = Object.values(nightLinePoints[l + 1])[0];
|
|
const y2: number = Object.values(nightLinePoints[l + 1])[1];
|
|
const lineString = [fromLonLat([x1, y1]), fromLonLat([x2, y2])];
|
|
lineStringArray.push(lineString);
|
|
}
|
|
nightLineLayer.getSource()?.addFeature(
|
|
new Feature({
|
|
geometry: new MultiLineString(lineStringArray),
|
|
})
|
|
);
|
|
|
|
let sunLinePos: number[] = [];
|
|
sunLinePos = sourceLineMethods.getSunPosition(lineTime);
|
|
sunLineFeature.getGeometry()?.setCoordinates(fromLonLat(sunLinePos));
|
|
sunLineFeature.setStyle([sunLineStyle, sunLineStyleDash]);
|
|
}
|
|
})
|
|
);
|
|
|
|
subscriptions.add(
|
|
eventBus.subscribe(DataHoverClearEvent, (event) => {
|
|
nightLineLayer.getSource()?.clear();
|
|
sunLineFeature.setStyle(new Style({}));
|
|
})
|
|
);
|
|
}
|
|
|
|
return {
|
|
init: () => layer,
|
|
dispose: () => subscriptions.unsubscribe(),
|
|
update: (data: PanelData) => {
|
|
const from = new Date(data.timeRange.from.valueOf());
|
|
const to = new Date(data.timeRange.to.valueOf());
|
|
let selectedTime: Date = new Date();
|
|
let sunPos: number[] = [];
|
|
// TODO: add option for "Both"
|
|
if (config.show === ShowTime.From) {
|
|
selectedTime = from;
|
|
} else {
|
|
selectedTime = to;
|
|
}
|
|
|
|
source.setTime(selectedTime);
|
|
if (config.sun) {
|
|
sunPos = sourceMethods.getSunPosition(selectedTime);
|
|
sunFeature.getGeometry()?.setCoordinates(fromLonLat(sunPos));
|
|
}
|
|
},
|
|
|
|
// Marker overlay options
|
|
registerOptionsUI: (builder) => {
|
|
if (!options.config?.nightColor) {
|
|
options.config = { ...defaultConfig, ...options.config };
|
|
}
|
|
|
|
builder.addRadio({
|
|
path: 'config.show',
|
|
name: 'Show',
|
|
settings: {
|
|
options: [
|
|
{ label: 'From', value: ShowTime.From },
|
|
{ label: 'To', value: ShowTime.To },
|
|
],
|
|
},
|
|
defaultValue: defaultConfig.show,
|
|
});
|
|
builder.addColorPicker({
|
|
path: 'config.nightColor',
|
|
name: 'Night region color',
|
|
description: 'Pick color of night region',
|
|
defaultValue: defaultConfig.nightColor,
|
|
settings: [{ enableNamedColors: false }],
|
|
});
|
|
builder.addBooleanSwitch({
|
|
path: 'config.sun',
|
|
name: 'Display sun',
|
|
description: 'Show the sun',
|
|
defaultValue: defaultConfig.sun,
|
|
});
|
|
},
|
|
};
|
|
},
|
|
|
|
// fill in the default values
|
|
defaultOptions: defaultConfig,
|
|
};
|