1import { FieldConfigSource, PanelModel, PanelTypeChangedHandler, Threshold, ThresholdsMode } from '@grafana/data';
2import { ResourceDimensionMode } from 'app/features/dimensions';
3import { cloneDeep } from 'lodash';
4import { MarkersConfig } from './layers/data/markersLayer';
5import { getMarkerAsPath } from './style/markers';
6import { defaultStyleConfig } from './style/types';
7import { GeomapPanelOptions } from './types';
8import { MapCenterID } from './view';
9
10/**
11 * This is called when the panel changes from another panel
12 */
13export const mapPanelChangedHandler: PanelTypeChangedHandler = (panel, prevPluginId, prevOptions, prevFieldConfig) => {
14  // Changing from angular/worldmap panel to react/openlayers
15  if (prevPluginId === 'grafana-worldmap-panel' && prevOptions.angular) {
16    const { fieldConfig, options } = worldmapToGeomapOptions({
17      ...prevOptions.angular,
18      fieldConfig: prevFieldConfig,
19    });
20    panel.fieldConfig = fieldConfig; // Mutates the incoming panel
21    return options;
22  }
23
24  return {};
25};
26
27export function worldmapToGeomapOptions(angular: any): { fieldConfig: FieldConfigSource; options: GeomapPanelOptions } {
28  const fieldConfig: FieldConfigSource = {
29    defaults: {},
30    overrides: [],
31  };
32
33  const options: GeomapPanelOptions = {
34    view: {
35      id: MapCenterID.Zero,
36    },
37    controls: {
38      showZoom: true,
39      mouseWheelZoom: Boolean(angular.mouseWheelZoom),
40    },
41    basemap: {
42      type: 'default', // was carto
43      name: 'Basemap',
44    },
45    layers: [
46      // TODO? depends on current configs
47    ],
48  };
49
50  let v = asNumber(angular.decimals);
51  if (v) {
52    fieldConfig.defaults.decimals = v;
53  }
54
55  // Convert thresholds and color values
56  if (angular.thresholds && angular.colors) {
57    const levels = angular.thresholds.split(',').map((strVale: string) => {
58      return Number(strVale.trim());
59    });
60
61    // One more color than threshold
62    const thresholds: Threshold[] = [];
63    for (const color of angular.colors) {
64      const idx = thresholds.length - 1;
65      if (idx >= 0) {
66        thresholds.push({ value: levels[idx], color });
67      } else {
68        thresholds.push({ value: -Infinity, color });
69      }
70    }
71
72    fieldConfig.defaults.thresholds = {
73      mode: ThresholdsMode.Absolute,
74      steps: thresholds,
75    };
76  }
77
78  v = asNumber(angular.initialZoom);
79  if (v) {
80    options.view.zoom = v;
81  }
82
83  // mapCenter: 'Europe',
84  // mapCenterLatitude: 46,
85  // mapCenterLongitude: 14,
86  //
87  // Map center (from worldmap)
88  const mapCenters: any = {
89    '(0°, 0°)': MapCenterID.Zero,
90    'North America': 'north-america',
91    Europe: 'europe',
92    'West Asia': 'west-asia',
93    'SE Asia': 'se-asia',
94    'Last GeoHash': MapCenterID.Coordinates, // MapCenterID.LastPoint,
95  };
96  options.view.id = mapCenters[angular.mapCenter as any];
97  options.view.lat = asNumber(angular.mapCenterLatitude);
98  options.view.lon = asNumber(angular.mapCenterLongitude);
99  return { fieldConfig, options };
100}
101
102function asNumber(v: any): number | undefined {
103  const num = +v;
104  return isNaN(num) ? undefined : num;
105}
106
107export const mapMigrationHandler = (panel: PanelModel): Partial<GeomapPanelOptions> => {
108  const pluginVersion = panel?.pluginVersion ?? '';
109
110  // before 8.3, only one layer was supported!
111  if (pluginVersion.startsWith('8.1') || pluginVersion.startsWith('8.2')) {
112    const layers = panel.options?.layers;
113    if (layers?.length === 1) {
114      const layer = panel.options.layers[0];
115      if (layer?.type === 'markers' && layer.config) {
116        // Moving style to child object
117        const oldConfig = layer.config;
118        const config: MarkersConfig = {
119          style: cloneDeep(defaultStyleConfig),
120          showLegend: Boolean(oldConfig.showLegend),
121        };
122
123        if (oldConfig.size) {
124          config.style.size = oldConfig.size;
125        }
126        if (oldConfig.color) {
127          config.style.color = oldConfig.color;
128        }
129        if (oldConfig.fillOpacity) {
130          config.style.opacity = oldConfig.fillOpacity;
131        }
132        const symbol = getMarkerAsPath(oldConfig.shape);
133        if (symbol) {
134          config.style.symbol = {
135            fixed: symbol,
136            mode: ResourceDimensionMode.Fixed,
137          };
138        }
139        return { ...panel.options, layers: [{ ...layer, config }] };
140      }
141    }
142  }
143  return panel.options;
144};
145