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/utils/utils.ts

193 lines
6.3 KiB

import { Map as OpenLayersMap } from 'ol';
import Geometry from 'ol/geom/Geometry';
import Point from 'ol/geom/Point';
import { defaults as interactionDefaults } from 'ol/interaction';
import BaseLayer from 'ol/layer/Base';
import LayerGroup from 'ol/layer/Group';
import ImageLayer from 'ol/layer/Image';
import TileLayer from 'ol/layer/Tile';
import VectorLayer from 'ol/layer/Vector';
import VectorImage from 'ol/layer/VectorImage';
import WebGLPointsLayer from 'ol/layer/WebGLPoints';
import ImageSource from 'ol/source/Image';
import TileSource from 'ol/source/Tile';
import VectorSource from 'ol/source/Vector';
import { DataFrame, GrafanaTheme2, SelectableValue } from '@grafana/data';
import { getColorDimension, getScalarDimension, getScaledDimension, getTextDimension } from 'app/features/dimensions';
import { getGrafanaDatasource } from 'app/plugins/datasource/grafana/datasource';
import { GeomapPanel } from '../GeomapPanel';
import { defaultStyleConfig, StyleConfig, StyleConfigState, StyleDimensions } from '../style/types';
import { Options, MapLayerState } from '../types';
export function getStyleDimension(
frame: DataFrame | undefined,
style: StyleConfigState,
theme: GrafanaTheme2,
customStyleConfig?: StyleConfig
) {
const dims: StyleDimensions = {};
if (customStyleConfig && Object.keys(customStyleConfig).length) {
dims.color = getColorDimension(frame, customStyleConfig.color ?? defaultStyleConfig.color, theme);
dims.size = getScaledDimension(frame, customStyleConfig.size ?? defaultStyleConfig.size);
dims.rotation = getScalarDimension(frame, customStyleConfig.rotation ?? defaultStyleConfig.rotation);
if (customStyleConfig.text && (customStyleConfig.text.field || customStyleConfig.text.fixed)) {
dims.text = getTextDimension(frame, customStyleConfig.text!);
}
} else {
if (style.fields) {
if (style.fields.color) {
dims.color = getColorDimension(frame, style.config.color ?? defaultStyleConfig.color, theme);
}
if (style.fields.size) {
dims.size = getScaledDimension(frame, style.config.size ?? defaultStyleConfig.size);
}
if (style.fields.text) {
dims.text = getTextDimension(frame, style.config.text!);
}
if (style.fields.rotation) {
dims.rotation = getScalarDimension(frame, style.config.rotation ?? defaultStyleConfig.rotation);
}
}
}
return dims;
}
let publicGeoJSONFiles: Array<SelectableValue<string>> | undefined = undefined;
export function getPublicGeoJSONFiles(): Array<SelectableValue<string>> {
if (!publicGeoJSONFiles) {
publicGeoJSONFiles = [];
initGeojsonFiles(); // async
}
return publicGeoJSONFiles;
}
// This will find all geojson files in the maps and gazetteer folders
async function initGeojsonFiles() {
const ds = await getGrafanaDatasource();
for (let folder of ['maps', 'gazetteer']) {
ds.listFiles(folder).subscribe({
next: (frame) => {
frame.forEach((item) => {
if (item.name.endsWith('.geojson')) {
const value = `public/${folder}/${item.name}`;
publicGeoJSONFiles!.push({
value,
label: value,
});
}
});
},
});
}
}
export const getNewOpenLayersMap = (panel: GeomapPanel, options: Options, div: HTMLDivElement) => {
const view = panel.initMapView(options.view);
return (panel.map = new OpenLayersMap({
view: view,
pixelRatio: 1, // or zoom?
layers: [], // loaded explicitly below
controls: [],
target: div,
interactions: interactionDefaults({
mouseWheelZoom: false, // managed by initControls
}),
}));
};
export const updateMap = (panel: GeomapPanel, options: Options) => {
panel.initControls(options.controls);
panel.forceUpdate(); // first render
};
export const notifyPanelEditor = (geomapPanel: GeomapPanel, layers: MapLayerState[], selected: number) => {
// Notify the panel editor
if (geomapPanel.panelContext && geomapPanel.panelContext.onInstanceStateChange) {
geomapPanel.panelContext.onInstanceStateChange({
map: geomapPanel.map,
layers: layers,
selected: selected,
actions: geomapPanel.actions,
});
}
};
export const getNextLayerName = (panel: GeomapPanel) => {
let idx = panel.layers.length; // since basemap is 0, this looks right
while (true && idx < 100) {
const name = `Layer ${idx++}`;
if (!panel.byName.has(name)) {
return name;
}
}
return `Layer ${Date.now()}`;
};
export function isSegmentVisible(
map: OpenLayersMap,
pixelTolerance: number,
segmentStartCoords: number[],
segmentEndCoords: number[]
): boolean {
// For a segment, calculate x and y pixel lengths
//TODO: let's try to find a less intensive check
const pixelStart = map.getPixelFromCoordinate(segmentStartCoords);
const pixelEnd = map.getPixelFromCoordinate(segmentEndCoords);
const deltaX = Math.abs(pixelStart[0] - pixelEnd[0]);
const deltaY = Math.abs(pixelStart[1] - pixelEnd[1]);
// If greater than pixel tolerance in either direction, segment is visible
if (deltaX > pixelTolerance || deltaY > pixelTolerance) {
return true;
}
return false;
}
export const isUrl = (url: string) => {
try {
const newUrl = new URL(url);
return newUrl.protocol.includes('http');
} catch (_) {
return false;
}
};
/**
* Checks if a layer has data to display
* @param layer The OpenLayers layer to check
* @returns boolean indicating if the layer has data
*/
export function hasLayerData(
layer:
| LayerGroup
| VectorLayer<VectorSource<Geometry>>
| VectorImage<VectorSource<Geometry>>
| WebGLPointsLayer<VectorSource<Point>>
| TileLayer<TileSource>
| ImageLayer<ImageSource>
| BaseLayer
): boolean {
if (layer instanceof LayerGroup) {
return layer
.getLayers()
.getArray()
.some((subLayer) => hasLayerData(subLayer));
}
if (layer instanceof VectorLayer || layer instanceof VectorImage) {
const source = layer.getSource();
return source != null && source.getFeatures().length > 0;
}
if (layer instanceof WebGLPointsLayer) {
const source = layer.getSource();
return source != null && source.getFeatures().length > 0;
}
if (layer instanceof TileLayer || layer instanceof ImageLayer) {
// For tile/image layers, check if they have a source
return Boolean(layer.getSource());
}
return false;
}