The open and composable observability and data visualization platform. Visualize metrics, logs, and traces from multiple sources like Prometheus, Loki, Elasticsearch, InfluxDB, Postgres and many more.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
grafana/public/app/plugins/panel/geomap/layers/data/dayNightLayer.tsx

260 lines
7.2 KiB

import {
MapLayerRegistryItem,
MapLayerOptions,
PanelData,
GrafanaTheme2,
EventBus,
DataHoverEvent,
DataHoverClearEvent,
} from '@grafana/data';
import Map from 'ol/Map';
import VectorLayer from 'ol/layer/Vector';
import VectorSource from 'ol/source/Vector';
import { Fill, Stroke, Style, Circle } from 'ol/style';
import {Group as LayerGroup} from 'ol/layer';
import Feature from 'ol/Feature';
import Point from 'ol/geom/Point';
import DayNight from 'ol-ext/source/DayNight';
import { fromLonLat } from 'ol/proj';
import { Subscription } from 'rxjs';
import { MultiLineString } from 'ol/geom';
import { Coordinate } from 'ol/coordinate';
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 VectorLayer({
source,
style: new Style({
fill: new Fill({
color: theme.visualization.getColorByName(config.nightColor)
})
})
});
// Night line (for crosshair sharing)
const nightLineLayer = new VectorLayer({
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 VectorLayer({
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 VectorLayer({
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 as number;
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,
};