mirror of
https://github.com/grafana/grafana.git
synced 2025-02-13 00:55:47 -06:00
216 lines
5.8 KiB
TypeScript
216 lines
5.8 KiB
TypeScript
import {
|
|
FrameGeometrySource,
|
|
FrameGeometrySourceMode,
|
|
FieldMatcher,
|
|
getFieldMatcher,
|
|
FieldMatcherID,
|
|
DataFrame,
|
|
Field,
|
|
getFieldDisplayName,
|
|
FieldType,
|
|
} from '@grafana/data';
|
|
import { Geometry } from 'ol/geom';
|
|
import { getGazetteer, Gazetteer } from '../gazetteer/gazetteer';
|
|
import { getGeoFieldFromGazetteer, pointFieldFromGeohash, pointFieldFromLonLat } from '../format/utils';
|
|
|
|
export type FieldFinder = (frame: DataFrame) => Field | undefined;
|
|
|
|
function getFieldFinder(matcher: FieldMatcher): FieldFinder {
|
|
return (frame: DataFrame) => {
|
|
for (const field of frame.fields) {
|
|
if (matcher(field, frame, [])) {
|
|
return field;
|
|
}
|
|
}
|
|
return undefined;
|
|
};
|
|
}
|
|
|
|
function matchLowerNames(names: Set<string>): FieldFinder {
|
|
return (frame: DataFrame) => {
|
|
for (const field of frame.fields) {
|
|
if (names.has(field.name.toLowerCase())) {
|
|
return field;
|
|
}
|
|
const disp = getFieldDisplayName(field, frame);
|
|
if (names.has(disp)) {
|
|
return field;
|
|
}
|
|
}
|
|
return undefined;
|
|
};
|
|
}
|
|
|
|
export interface LocationFieldMatchers {
|
|
mode: FrameGeometrySourceMode;
|
|
|
|
// Field mappings
|
|
geohash: FieldFinder;
|
|
latitude: FieldFinder;
|
|
longitude: FieldFinder;
|
|
h3: FieldFinder;
|
|
wkt: FieldFinder;
|
|
lookup: FieldFinder;
|
|
geo: FieldFinder;
|
|
gazetteer?: Gazetteer;
|
|
}
|
|
|
|
const defaultMatchers: LocationFieldMatchers = {
|
|
mode: FrameGeometrySourceMode.Auto,
|
|
geohash: matchLowerNames(new Set(['geohash'])),
|
|
latitude: matchLowerNames(new Set(['latitude', 'lat'])),
|
|
longitude: matchLowerNames(new Set(['longitude', 'lon', 'lng'])),
|
|
h3: matchLowerNames(new Set(['h3'])),
|
|
wkt: matchLowerNames(new Set(['wkt'])),
|
|
lookup: matchLowerNames(new Set(['lookup'])),
|
|
geo: (frame: DataFrame) => frame.fields.find((f) => f.type === FieldType.geo),
|
|
};
|
|
|
|
export async function getLocationMatchers(src?: FrameGeometrySource): Promise<LocationFieldMatchers> {
|
|
const info: LocationFieldMatchers = {
|
|
...defaultMatchers,
|
|
mode: src?.mode ?? FrameGeometrySourceMode.Auto,
|
|
};
|
|
switch (info.mode) {
|
|
case FrameGeometrySourceMode.Geohash:
|
|
if (src?.geohash) {
|
|
info.geohash = getFieldFinder(getFieldMatcher({ id: FieldMatcherID.byName, options: src.geohash }));
|
|
}
|
|
break;
|
|
case FrameGeometrySourceMode.Lookup:
|
|
if (src?.lookup) {
|
|
info.lookup = getFieldFinder(getFieldMatcher({ id: FieldMatcherID.byName, options: src.lookup }));
|
|
}
|
|
info.gazetteer = await getGazetteer(src?.gazetteer);
|
|
break;
|
|
case FrameGeometrySourceMode.Coords:
|
|
if (src?.latitude) {
|
|
info.latitude = getFieldFinder(getFieldMatcher({ id: FieldMatcherID.byName, options: src.latitude }));
|
|
}
|
|
if (src?.longitude) {
|
|
info.longitude = getFieldFinder(getFieldMatcher({ id: FieldMatcherID.byName, options: src.longitude }));
|
|
}
|
|
break;
|
|
}
|
|
return info;
|
|
}
|
|
export interface LocationFields {
|
|
mode: FrameGeometrySourceMode;
|
|
|
|
// Field mappings
|
|
geohash?: Field;
|
|
latitude?: Field;
|
|
longitude?: Field;
|
|
h3?: Field;
|
|
wkt?: Field;
|
|
lookup?: Field;
|
|
geo?: Field<Geometry>;
|
|
}
|
|
|
|
export function getLocationFields(frame: DataFrame, location: LocationFieldMatchers): LocationFields {
|
|
const fields: LocationFields = {
|
|
mode: location.mode ?? FrameGeometrySourceMode.Auto,
|
|
};
|
|
|
|
// Find the best option
|
|
if (fields.mode === FrameGeometrySourceMode.Auto) {
|
|
fields.geo = location.geo(frame);
|
|
if (fields.geo) {
|
|
return fields;
|
|
}
|
|
|
|
fields.latitude = location.latitude(frame);
|
|
fields.longitude = location.longitude(frame);
|
|
if (fields.latitude && fields.longitude) {
|
|
fields.mode = FrameGeometrySourceMode.Coords;
|
|
return fields;
|
|
}
|
|
fields.geohash = location.geohash(frame);
|
|
if (fields.geohash) {
|
|
fields.mode = FrameGeometrySourceMode.Geohash;
|
|
return fields;
|
|
}
|
|
fields.lookup = location.geohash(frame);
|
|
if (fields.lookup) {
|
|
fields.mode = FrameGeometrySourceMode.Lookup;
|
|
return fields;
|
|
}
|
|
}
|
|
|
|
switch (fields.mode) {
|
|
case FrameGeometrySourceMode.Coords:
|
|
fields.latitude = location.latitude(frame);
|
|
fields.longitude = location.longitude(frame);
|
|
break;
|
|
case FrameGeometrySourceMode.Geohash:
|
|
fields.geohash = location.geohash(frame);
|
|
break;
|
|
case FrameGeometrySourceMode.Lookup:
|
|
fields.lookup = location.lookup(frame);
|
|
break;
|
|
}
|
|
|
|
return fields;
|
|
}
|
|
|
|
export interface FrameGeometryField {
|
|
field?: Field<Geometry | undefined>;
|
|
warning?: string;
|
|
derived?: boolean;
|
|
}
|
|
|
|
export function getGeometryField(frame: DataFrame, location: LocationFieldMatchers): FrameGeometryField {
|
|
const fields = getLocationFields(frame, location);
|
|
switch (fields.mode) {
|
|
case FrameGeometrySourceMode.Auto:
|
|
if (fields.geo) {
|
|
return {
|
|
field: fields.geo,
|
|
};
|
|
}
|
|
return {
|
|
warning: 'Unable to find location fields',
|
|
};
|
|
|
|
case FrameGeometrySourceMode.Coords:
|
|
if (fields.latitude && fields.longitude) {
|
|
return {
|
|
field: pointFieldFromLonLat(fields.longitude, fields.latitude),
|
|
derived: true,
|
|
};
|
|
}
|
|
return {
|
|
warning: 'Missing latitude/longitude fields',
|
|
};
|
|
|
|
case FrameGeometrySourceMode.Geohash:
|
|
if (fields.geohash) {
|
|
return {
|
|
field: pointFieldFromGeohash(fields.geohash),
|
|
derived: true,
|
|
};
|
|
}
|
|
return {
|
|
warning: 'Missing geohash field',
|
|
};
|
|
|
|
case FrameGeometrySourceMode.Lookup:
|
|
if (fields.lookup) {
|
|
if (location.gazetteer) {
|
|
return {
|
|
field: getGeoFieldFromGazetteer(location.gazetteer, fields.lookup),
|
|
derived: true,
|
|
};
|
|
}
|
|
return {
|
|
warning: 'Gazetteer not found',
|
|
};
|
|
}
|
|
return {
|
|
warning: 'Missing lookup field',
|
|
};
|
|
}
|
|
|
|
return { warning: 'unable to find geometry' };
|
|
}
|