Geomap: improve the view configuration (#36893)

This commit is contained in:
Ryan McKinley 2021-07-19 08:40:56 -07:00 committed by GitHub
parent 001331e2ac
commit ee3a320540
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 151 additions and 147 deletions

View File

@ -31,6 +31,7 @@ interface MapLayerState {
// Allows multiple panels to share the same view instance
let sharedView: View | undefined = undefined;
export let lastGeomapPanelInstance: GeomapPanel | undefined = undefined;
type Props = PanelProps<GeomapPanelOptions>;
export class GeomapPanel extends Component<Props> {
@ -43,6 +44,10 @@ export class GeomapPanel extends Component<Props> {
style = getStyles(config.theme);
overlayProps: OverlayProps = {};
componentDidMount() {
lastGeomapPanelInstance = this;
}
shouldComponentUpdate(nextProps: Props) {
if (!this.map) {
return true; // not yet initalized
@ -206,13 +211,12 @@ export class GeomapPanel extends Component<Props> {
}
}
const v = centerPointRegistry.getIfExists(config.center.id);
const v = centerPointRegistry.getIfExists(config.id);
if (v) {
let coord: Coordinate | undefined = undefined;
if (v.lat == null) {
if (v.id === MapCenterID.Coordinates) {
const center = config.center ?? {};
coord = [center.lon ?? 0, center.lat ?? 0];
coord = [config.lon ?? 0, config.lat ?? 0];
} else {
console.log('TODO, view requires special handling', v);
}

View File

@ -1,85 +0,0 @@
import React, { FC, useMemo } from 'react';
import { GrafanaTheme, StandardEditorProps } from '@grafana/data';
import { Select, stylesFactory, useStyles } from '@grafana/ui';
import { GeomapPanelOptions, MapCenterConfig } from '../types';
import { centerPointRegistry, MapCenterID } from '../view';
import { css } from '@emotion/css';
import { NumberInput } from '../components/NumberInput';
export const MapCenterEditor: FC<StandardEditorProps<MapCenterConfig, any, GeomapPanelOptions>> = ({
value,
onChange,
context,
}) => {
const style = useStyles(getStyles);
const views = useMemo(() => {
const ids: string[] = [];
if (value?.id) {
ids.push(value.id);
} else {
ids.push(centerPointRegistry.list()[0].id);
}
return centerPointRegistry.selectOptions(ids);
}, [value?.id]);
return (
<div>
<Select
options={views.options}
value={views.current}
onChange={(v) => {
onChange({
id: v.value!,
});
}}
/>
{value?.id === MapCenterID.Coordinates && (
<div>
<table className={style.table}>
<tbody>
<tr>
<th className={style.half}>Latitude</th>
<th className={style.half}>Longitude</th>
</tr>
<tr>
<td>
<NumberInput
value={value.lat}
min={-90}
max={90}
placeholder="0"
onChange={(v) => {
onChange({ ...value, lat: v });
}}
/>
</td>
<td>
<NumberInput
value={value.lon}
min={-180}
max={180}
placeholder="0"
onChange={(v) => {
onChange({ ...value, lon: v });
}}
/>
</td>
</tr>
</tbody>
</table>
</div>
)}
</div>
);
};
const getStyles = stylesFactory((theme: GrafanaTheme) => ({
table: css`
width: 100%;
margin-top: 8px;
`,
half: css`
width: 50%;
`,
}));

View File

@ -0,0 +1,120 @@
import React, { FC, useMemo, useCallback } from 'react';
import { StandardEditorProps, SelectableValue } from '@grafana/data';
import { Button, InlineField, InlineFieldRow, Select, VerticalGroup } from '@grafana/ui';
import { GeomapPanelOptions, MapViewConfig } from '../types';
import { centerPointRegistry, MapCenterID } from '../view';
import { NumberInput } from '../components/NumberInput';
import { lastGeomapPanelInstance } from '../GeomapPanel';
import { toLonLat } from 'ol/proj';
export const MapViewEditor: FC<StandardEditorProps<MapViewConfig, any, GeomapPanelOptions>> = ({
value,
onChange,
context,
}) => {
const labelWidth = 10;
const views = useMemo(() => {
const ids: string[] = [];
if (value?.id) {
ids.push(value.id);
} else {
ids.push(centerPointRegistry.list()[0].id);
}
return centerPointRegistry.selectOptions(ids);
}, [value?.id]);
const onSetCurrentView = useCallback(() => {
const map = lastGeomapPanelInstance?.map;
if (map) {
const view = map.getView();
const coords = view.getCenter();
if (coords) {
const center = toLonLat(coords, view.getProjection());
onChange({
...value,
id: MapCenterID.Coordinates,
lon: +center[0].toFixed(6),
lat: +center[1].toFixed(6),
zoom: +view.getZoom()!.toFixed(2),
});
}
}
}, [value, onChange]);
const onSelectView = useCallback(
(selection: SelectableValue<string>) => {
const v = centerPointRegistry.getIfExists(selection.value);
if (v) {
onChange({
...value,
id: v.id,
lat: v.lat ?? value.lat,
lon: v.lon ?? value.lon,
zoom: v.zoom ?? value.zoom,
});
}
},
[value, onChange]
);
return (
<>
<InlineFieldRow>
<InlineField label="View" labelWidth={labelWidth} grow={true}>
<Select options={views.options} value={views.current} onChange={onSelectView} />
</InlineField>
</InlineFieldRow>
{value?.id === MapCenterID.Coordinates && (
<>
<InlineFieldRow>
<InlineField label="Latitude" labelWidth={labelWidth} grow={true}>
<NumberInput
value={value.lat}
min={-90}
max={90}
step={0.001}
onChange={(v) => {
onChange({ ...value, lat: v });
}}
/>
</InlineField>
</InlineFieldRow>
<InlineFieldRow>
<InlineField label="Longitude" labelWidth={labelWidth} grow={true}>
<NumberInput
value={value.lon}
min={-180}
max={180}
step={0.001}
onChange={(v) => {
onChange({ ...value, lon: v });
}}
/>
</InlineField>
</InlineFieldRow>
</>
)}
<InlineFieldRow>
<InlineField label="Zoom" labelWidth={labelWidth} grow={true}>
<NumberInput
value={value.zoom ?? 1}
min={1}
max={18}
step={0.01}
onChange={(v) => {
onChange({ ...value, zoom: v });
}}
/>
</InlineField>
</InlineFieldRow>
<VerticalGroup>
<Button variant="secondary" size="sm" fullWidth onClick={onSetCurrentView}>
<span>Use current map settings</span>
</Button>
</VerticalGroup>
</>
);
};

View File

@ -1,18 +0,0 @@
import React, { FC } from 'react';
import { StandardEditorProps } from '@grafana/data';
import { GeomapPanelOptions } from '../types';
import { NumberInput } from '../components/NumberInput';
export const MapZoomEditor: FC<StandardEditorProps<number | undefined, any, GeomapPanelOptions>> = ({
value,
onChange,
context,
}) => {
// TODO:
// Somehow use context to get the current map and listen to zoom changes
return (
<div>
<NumberInput value={value} min={1} max={30} onChange={onChange} />
</div>
);
};

View File

@ -57,11 +57,9 @@ describe('Worldmap Migrations', () => {
},
"layers": Array [],
"view": Object {
"center": Object {
"id": "europe",
"lat": 46,
"lon": 14,
},
"id": "europe",
"lat": 46,
"lon": 14,
"zoom": 6,
},
},

View File

@ -27,9 +27,7 @@ export function worldmapToGeomapOptions(angular: any): { fieldConfig: FieldConfi
const options: GeomapPanelOptions = {
view: {
center: {
id: MapCenterID.Zero,
},
id: MapCenterID.Zero,
},
controls: {
showZoom: true,
@ -88,11 +86,11 @@ export function worldmapToGeomapOptions(angular: any): { fieldConfig: FieldConfi
Europe: 'europe',
'West Asia': 'west-asia',
'SE Asia': 'se-asia',
'Last GeoHash': MapCenterID.LastPoint,
'Last GeoHash': MapCenterID.Coordinates, // MapCenterID.LastPoint,
};
options.view.center.id = mapCenters[angular.mapCenter as any];
options.view.center.lat = asNumber(angular.mapCenterLatitude);
options.view.center.lon = asNumber(angular.mapCenterLongitude);
options.view.id = mapCenters[angular.mapCenter as any];
options.view.lat = asNumber(angular.mapCenterLatitude);
options.view.lon = asNumber(angular.mapCenterLongitude);
return { fieldConfig, options };
}

View File

@ -2,9 +2,8 @@ import { FrameGeometrySourceMode, PanelPlugin } from '@grafana/data';
import { BaseLayerEditor } from './editor/BaseLayerEditor';
import { DataLayersEditor } from './editor/DataLayersEditor';
import { GeomapPanel } from './GeomapPanel';
import { MapCenterEditor } from './editor/MapCenterEditor';
import { MapViewEditor } from './editor/MapViewEditor';
import { defaultView, GeomapPanelOptions } from './types';
import { MapZoomEditor } from './editor/MapZoomEditor';
import { mapPanelChangedHandler } from './migrations';
import { defaultGrafanaThemedMap } from './layers/basemaps';
import { MARKERS_LAYER_ID } from './layers/data/markersLayer';
@ -17,20 +16,12 @@ export const plugin = new PanelPlugin<GeomapPanelOptions>(GeomapPanel)
let category = ['Map View'];
builder.addCustomEditor({
category,
id: 'view.center',
path: 'view.center',
name: 'Center',
editor: MapCenterEditor,
defaultValue: defaultView.center,
});
builder.addCustomEditor({
category,
id: 'view.zoom',
path: 'view.zoom',
name: 'Initial zoom',
editor: MapZoomEditor,
defaultValue: defaultView.zoom,
id: 'view',
path: 'view',
name: 'Initial view', // don't show it
description: 'This location will show when the panel first loads',
editor: MapViewEditor,
defaultValue: defaultView,
});
builder.addBooleanSwitch({

View File

@ -23,14 +23,10 @@ export interface ControlsOptions {
showDebug?: boolean;
}
export interface MapCenterConfig {
export interface MapViewConfig {
id: string; // placename > lookup
lat?: number;
lon?: number;
}
export interface MapViewConfig {
center: MapCenterConfig;
zoom?: number;
minZoom?: number;
maxZoom?: number;
@ -38,9 +34,9 @@ export interface MapViewConfig {
}
export const defaultView: MapViewConfig = {
center: {
id: MapCenterID.Zero,
},
id: MapCenterID.Zero,
lat: 0,
lon: 0,
zoom: 1,
};

View File

@ -3,12 +3,12 @@ import { Registry, RegistryItem } from '@grafana/data';
interface MapCenterItems extends RegistryItem {
lat?: number;
lon?: number;
zoom?: number;
}
export enum MapCenterID {
Zero = 'zero',
Coordinates = 'coords',
LastPoint = 'last',
}
export const centerPointRegistry = new Registry<MapCenterItems>(() => [
@ -23,31 +23,31 @@ export const centerPointRegistry = new Registry<MapCenterItems>(() => [
name: 'North America',
lat: 40,
lon: -100,
zoom: 4,
},
{
id: 'europe',
name: 'Europe',
lat: 46,
lon: 14,
zoom: 4,
},
{
id: 'west-asia',
name: 'West Asia',
lat: 26,
lon: 53,
zoom: 4,
},
{
id: 'se-asia',
name: 'South-east Asia',
lat: 10,
lon: 106,
zoom: 4,
},
{
id: MapCenterID.Coordinates as string,
name: 'Coordinates',
},
{
id: MapCenterID.LastPoint as string,
name: 'Last value',
},
]);