spatialTransformer.ts 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990
  1. import { mergeMap, from } from 'rxjs';
  2. import { ArrayVector, DataFrame, DataTransformerID, DataTransformerInfo, FieldType } from '@grafana/data';
  3. import { createGeometryCollection, createLineBetween } from 'app/features/geo/format/utils';
  4. import { getGeometryField, getLocationMatchers } from 'app/features/geo/utils/location';
  5. import { SpatialOperation, SpatialAction, SpatialTransformOptions } from './models.gen';
  6. import { doGeomeryCalculation, toLineString } from './utils';
  7. export const spatialTransformer: DataTransformerInfo<SpatialTransformOptions> = {
  8. id: DataTransformerID.spatial,
  9. name: 'Spatial operations',
  10. description: 'Apply spatial operations to query results',
  11. defaultOptions: {},
  12. operator: (options) => (source) => source.pipe(mergeMap((data) => from(doSetGeometry(data, options)))),
  13. };
  14. export function isLineBuilderOption(options: SpatialTransformOptions): boolean {
  15. return options.action === SpatialAction.Modify && options.modify?.op === SpatialOperation.LineBuilder;
  16. }
  17. async function doSetGeometry(frames: DataFrame[], options: SpatialTransformOptions): Promise<DataFrame[]> {
  18. const location = await getLocationMatchers(options.source);
  19. if (isLineBuilderOption(options)) {
  20. const targetLocation = await getLocationMatchers(options.modify?.target);
  21. return frames.map((frame) => {
  22. const src = getGeometryField(frame, location);
  23. const target = getGeometryField(frame, targetLocation);
  24. if (src.field && target.field) {
  25. const fields = [...frame.fields];
  26. const line = createLineBetween(src.field, target.field);
  27. const first = fields[0];
  28. if (first.type === FieldType.geo && first !== src.field && first !== target.field) {
  29. fields[0] = createGeometryCollection(first, line); //
  30. } else {
  31. fields.unshift(line);
  32. }
  33. return {
  34. ...frame,
  35. fields,
  36. };
  37. }
  38. return frame;
  39. });
  40. }
  41. return frames.map((frame) => {
  42. let info = getGeometryField(frame, location);
  43. if (info.field) {
  44. if (options.action === SpatialAction.Modify) {
  45. switch (options.modify?.op) {
  46. // SOON: extent, convex hull, etc
  47. case SpatialOperation.AsLine:
  48. let name = info.field.name;
  49. if (!name || name === 'Point') {
  50. name = 'Line';
  51. }
  52. return {
  53. ...frame,
  54. length: 1,
  55. fields: [
  56. {
  57. ...info.field,
  58. name,
  59. type: FieldType.geo,
  60. values: new ArrayVector([toLineString(info.field)]),
  61. },
  62. ],
  63. };
  64. }
  65. return frame;
  66. }
  67. const fields = info.derived ? [info.field, ...frame.fields] : frame.fields.slice(0);
  68. if (options.action === SpatialAction.Calculate) {
  69. fields.push(doGeomeryCalculation(info.field, options.calculate ?? {}));
  70. info.derived = true;
  71. }
  72. if (info.derived) {
  73. return {
  74. ...frame,
  75. fields,
  76. };
  77. }
  78. }
  79. return frame;
  80. });
  81. }