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/heatMap.tsx

162 lines
4.7 KiB

import {
FieldType,
getFieldColorModeForField,
GrafanaTheme2,
MapLayerOptions,
MapLayerRegistryItem,
PanelData,
} from '@grafana/data';
import Map from 'ol/Map';
import Feature from 'ol/Feature';
import * as layer from 'ol/layer';
import * as source from 'ol/source';
import { dataFrameToPoints, getLocationMatchers } from '../../utils/location';
import { ScaleDimensionConfig, getScaledDimension } from 'app/features/dimensions';
import { ScaleDimensionEditor } from 'app/features/dimensions/editors';
// Configuration options for Heatmap overlays
export interface HeatmapConfig {
weight: ScaleDimensionConfig;
blur: number;
radius: number;
}
const defaultOptions: HeatmapConfig = {
weight: {
fixed: 1,
min: 0,
max: 1,
},
blur: 15,
radius: 5,
};
/**
* Map layer configuration for heatmap overlay
*/
export const heatmapLayer: MapLayerRegistryItem<HeatmapConfig> = {
id: 'heatmap',
name: 'Heatmap',
description: 'visualizes a heatmap of the data',
isBaseMap: false,
showLocation: true,
/**
* Function that configures transformation and returns a transformer
* @param options
*/
create: async (map: Map, options: MapLayerOptions<HeatmapConfig>, theme: GrafanaTheme2) => {
const config = { ...defaultOptions, ...options.config };
const matchers = await getLocationMatchers(options.location);
const vectorSource = new source.Vector();
// Create a new Heatmap layer
// Weight function takes a feature as attribute and returns a normalized weight value
const vectorLayer = new layer.Heatmap({
source: vectorSource,
blur: config.blur,
radius: config.radius,
weight: function (feature) {
var weight = feature.get('value');
return weight;
},
});
return {
init: () => vectorLayer,
update: (data: PanelData) => {
const frame = data.series[0];
// Remove previous data before updating
const features = vectorLayer.getSource().getFeatures();
features.forEach((feature) => {
vectorLayer.getSource().removeFeature(feature);
});
if (!frame) {
return;
}
// Get data points (latitude and longitude coordinates)
const info = dataFrameToPoints(frame, matchers);
if (info.warning) {
console.log('WARN', info.warning);
return; // ???
}
const weightDim = getScaledDimension(frame, config.weight);
// Map each data value into new points
for (let i = 0; i < frame.length; i++) {
const cluster = new Feature({
geometry: info.points[i],
value: weightDim.get(i),
});
vectorSource.addFeature(cluster);
}
vectorLayer.setSource(vectorSource);
// Set heatmap gradient colors
let colors = ['#00f', '#0ff', '#0f0', '#ff0', '#f00'];
// Either the configured field or the first numeric field value
const field = weightDim.field ?? frame.fields.find((field) => field.type === FieldType.number);
if (field) {
const colorMode = getFieldColorModeForField(field);
if (colorMode.isContinuous && colorMode.getColors) {
// getColors return an array of color string from the color scheme chosen
colors = colorMode.getColors(theme);
}
}
vectorLayer.setGradient(colors);
},
// Heatmap overlay options
registerOptionsUI: (builder) => {
builder
.addCustomEditor({
id: 'config.weight',
path: 'config.weight',
name: 'Weight values',
description: 'Scale the distribution for each row',
editor: ScaleDimensionEditor,
settings: {
min: 0, // no contribution
max: 1,
hideRange: true, // Don't show the scale factor
},
defaultValue: {
// Configured values
fixed: 1,
min: 0,
max: 1,
},
})
.addSliderInput({
path: 'config.radius',
description: 'configures the size of clusters',
name: 'Radius',
defaultValue: defaultOptions.radius,
settings: {
min: 1,
max: 50,
step: 1,
},
})
.addSliderInput({
path: 'config.blur',
description: 'configures the amount of blur of clusters',
name: 'Blur',
defaultValue: defaultOptions.blur,
settings: {
min: 1,
max: 50,
step: 1,
},
});
},
};
},
// fill in the default values
defaultOptions,
};