Skip to content

Commit

Permalink
region_map vis with Map embeddable
Browse files Browse the repository at this point in the history
  • Loading branch information
nreese committed Jul 14, 2021
1 parent e0f1693 commit fabb710
Show file tree
Hide file tree
Showing 16 changed files with 415 additions and 36 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,19 @@ import { getJoinAggKey } from '../../../common/get_agg_key';

const defaultDynamicProperties = getDefaultDynamicProperties();

export interface CreateRegionMapLayerDescriptorParams {
label: string;
emsLayerId?: string;
leftFieldName?: string;
termsFieldName?: string;
termsSize?: number;
colorSchema: string;
indexPatternId?: string;
indexPatternTitle?: string;
metricAgg: string;
metricFieldName?: string;
}

export function createAggDescriptor(metricAgg: string, metricFieldName?: string): AggDescriptor {
const aggTypeKey = Object.keys(AGG_TYPE).find((key) => {
return AGG_TYPE[key as keyof typeof AGG_TYPE] === metricAgg;
Expand All @@ -55,18 +68,7 @@ export function createRegionMapLayerDescriptor({
indexPatternTitle,
metricAgg,
metricFieldName,
}: {
label: string;
emsLayerId?: string;
leftFieldName?: string;
termsFieldName?: string;
termsSize?: number;
colorSchema: string;
indexPatternId?: string;
indexPatternTitle?: string;
metricAgg: string;
metricFieldName?: string;
}): LayerDescriptor | null {
}: CreateRegionMapLayerDescriptorParams): LayerDescriptor | null {
if (!indexPatternId || !emsLayerId || !leftFieldName || !termsFieldName) {
return null;
}
Expand Down
14 changes: 2 additions & 12 deletions x-pack/plugins/maps/public/lazy_load_bundle/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { LayerWizard } from '../classes/layers/layer_wizard_registry';
import type { CreateLayerDescriptorParams } from '../classes/sources/es_search_source';
import type { EMSTermJoinConfig, SampleValuesConfig } from '../ems_autosuggest';
import type { CreateTileMapLayerDescriptorParams } from '../classes/layers/create_tile_map_layer_descriptor';
import type { CreateRegionMapLayerDescriptorParams } from '../classes/layers/create_region_map_layer_descriptor';

let loadModulesPromise: Promise<LazyLoadedMapModules>;

Expand Down Expand Up @@ -54,18 +55,7 @@ interface LazyLoadedMapModules {
indexPatternTitle,
metricAgg,
metricFieldName,
}: {
label: string;
emsLayerId?: string;
leftFieldName?: string;
termsFieldName?: string;
termsSize?: number;
colorSchema: string;
indexPatternId?: string;
indexPatternTitle?: string;
metricAgg: string;
metricFieldName?: string;
}) => LayerDescriptor | null;
}: CreateRegionMapLayerDescriptorParams) => LayerDescriptor | null;
createBasemapLayerDescriptor: () => LayerDescriptor | null;
createESSearchSourceLayerDescriptor: (params: CreateLayerDescriptorParams) => LayerDescriptor;
suggestEMSTermJoinConfig: (config: SampleValuesConfig) => Promise<EMSTermJoinConfig | null>;
Expand Down
1 change: 1 addition & 0 deletions x-pack/plugins/maps/public/legacy_visualizations/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@
* 2.0.
*/

export { createRegionMapFn, regionMapRenderer, regionMapVisType } from './region_map';
export { createTileMapFn, tileMapRenderer, tileMapVisType } from './tile_map';
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

export { regionMapVisType } from './region_map_vis_type';
export { createRegionMapFn } from './region_map_fn';
export { regionMapRenderer } from './region_map_renderer';
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import React from 'react';
import type { VisEditorOptionsProps } from 'src/plugins/visualizations/public';
import { Vis } from '../../../../../../src/plugins/visualizations/public';
import { getData, getShareService } from '../../kibana_services';
import { ViewInMaps } from '../view_in_maps';
import { extractLayerDescriptorParams } from './utils';
import { RegionMapVisParams } from './types';
import { title } from './region_map_vis_type';

export function RegionMapEditor(props: VisEditorOptionsProps) {
const onClick = (e: React.MouseEvent<HTMLButtonElement>) => {
e.preventDefault();

const locator = getShareService().url.locators.get('MAPS_APP_REGION_MAP_LOCATOR');
if (!locator) return;

const query = getData().query;
locator.navigate({
...extractLayerDescriptorParams((props.vis as unknown) as Vis<RegionMapVisParams>),
filters: query.filterManager.getFilters(),
query: query.queryString.getQuery(),
timeRange: query.timefilter.timefilter.getTime(),
});
};

return <ViewInMaps onClick={onClick} visualizationLabel={title} />;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import { i18n } from '@kbn/i18n';
import type { Filter, Query, TimeRange } from '../../../../../../src/plugins/data/common';
import type { ExpressionValueSearchContext } from '../../../../../../src/plugins/data/common/search/expressions/kibana_context_type';
import type {
ExpressionFunctionDefinition,
Render,
} from '../../../../../../src/plugins/expressions/public';
import { RegionMapVisConfig } from './types';

interface Arguments {
visConfig: string;
}

export interface RegionMapVisRenderValue {
visType: 'region_map';
visConfig: RegionMapVisConfig;
filters?: Filter[];
query?: Query;
timeRange?: TimeRange;
}

export type RegionMapExpressionFunctionDefinition = ExpressionFunctionDefinition<
'regionmap',
ExpressionValueSearchContext,
Arguments,
Promise<Render<RegionMapVisRenderValue>>
>;

export const createRegionMapFn = (): RegionMapExpressionFunctionDefinition => ({
name: 'regionmap',
type: 'render',
help: i18n.translate('xpack.maps.regionMap.function.help', {
defaultMessage: 'Regionmap visualization',
}),
args: {
visConfig: {
types: ['string'],
default: '"{}"',
help: '',
},
},
async fn(input, args) {
return {
type: 'render',
as: 'region_map_vis',
value: {
visType: 'region_map',
visConfig: JSON.parse(args.visConfig),
filters: input.filters,
query: Array.isArray(input.query) ? input.query[0] : input.query,
timeRange: input.timeRange,
},
};
},
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import React from 'react';
import { render, unmountComponentAtNode } from 'react-dom';
import type { ExpressionRenderDefinition } from 'src/plugins/expressions';
import { RegionMapVisRenderValue } from './region_map_fn';
import { RegionMapVisualization } from './region_map_visualization';

export const regionMapRenderer = {
name: 'region_map_vis',
reuseDomNode: true,
render: async (domNode, { filters, query, timeRange, visConfig }, handlers) => {
handlers.onDestroy(() => {
unmountComponentAtNode(domNode);
});

render(
<RegionMapVisualization
filters={filters}
query={query}
timeRange={timeRange}
visConfig={visConfig}
/>,
domNode
);
},
} as ExpressionRenderDefinition<RegionMapVisRenderValue>;
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import { i18n } from '@kbn/i18n';
import { VisTypeDefinition } from '../../../../../../src/plugins/visualizations/public';
import { toExpressionAst } from './to_ast';
import { RegionMapVisParams } from './types';
import { RegionMapEditor } from './region_map_editor';

export const title = i18n.translate('xpack.maps.regionMapMap.vis.title', {
defaultMessage: 'Region Map',
});

export const regionMapVisType = {
name: 'region_map',
title,
icon: 'visMapRegion',
description: i18n.translate('xpack.maps.regionMap.vis.description', {
defaultMessage: 'Show metrics on a thematic map. Use one of the \
provided base maps, or add your own. Darker colors represent higher values.',
}),
editorConfig: {
optionTabs: [
{
name: '',
title: '',
editor: RegionMapEditor,
},
],
},
visConfig: {
defaults: {
colorSchema: 'Yellow to Red',
mapZoom: 2,
mapCenter: [0, 0],
},
},
toExpressionAst,
requiresSearch: true,
} as VisTypeDefinition<RegionMapVisParams>;
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import React, { Component, RefObject } from 'react';
import uuid from 'uuid/v4';
import { EuiLoadingChart } from '@elastic/eui';
import type { Filter, Query, TimeRange } from '../../../../../../src/plugins/data/common';
import type { Embeddable } from '../../../../../../src/plugins/embeddable/public';
import type { MapEmbeddableInput, MapEmbeddableOutput } from '../../embeddable';
import { lazyLoadMapModules } from '../../lazy_load_bundle';
import { RegionMapVisConfig } from './types';

interface Props {
filters?: Filter[];
query?: Query;
timeRange?: TimeRange;
visConfig: RegionMapVisConfig;
}

interface State {
isLoaded: boolean;
}

export class RegionMapVisualization extends Component<Props, State> {
private _isMounted = false;
private _mapEmbeddable?: Embeddable<MapEmbeddableInput, MapEmbeddableOutput> | undefined;
private readonly _embeddableRef: RefObject<HTMLDivElement> = React.createRef<HTMLDivElement>();

state: State = { isLoaded: false };

componentDidMount() {
this._isMounted = true;
this._load();
}

componentWillUnmount() {
this._isMounted = false;
if (this._mapEmbeddable) {
this._mapEmbeddable.destroy();
}
}

componentDidUpdate() {
if (this._mapEmbeddable) {
this._mapEmbeddable.updateInput({
filters: this.props.filters,
query: this.props.query,
timeRange: this.props.timeRange,
});
}
}

async _load() {
const mapModules = await lazyLoadMapModules();
if (!this._isMounted) {
return;
}

this.setState({ isLoaded: true });

this._mapEmbeddable = new mapModules.MapEmbeddable(
{
editable: false,
},
{
id: uuid(),
attributes: {
title: '',
layerListJSON: JSON.stringify([
mapModules.createBasemapLayerDescriptor(),
mapModules.createRegionMapLayerDescriptor(this.props.visConfig.layerDescriptorParams),
]),
},
mapCenter: {
lat: this.props.visConfig.mapCenter[0],
lon: this.props.visConfig.mapCenter[1],
zoom: this.props.visConfig.mapZoom,
},
}
);
if (this._embeddableRef.current) {
this._mapEmbeddable.render(this._embeddableRef.current);
}
}

render() {
if (!this.state.isLoaded) {
return <EuiLoadingChart mono size="l" />;
}

return <div className="mapLegacyVisualizationContainer" ref={this._embeddableRef} />;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import {
buildExpression,
buildExpressionFunction,
} from '../../../../../../src/plugins/expressions/public';
import { VisToExpressionAst } from '../../../../../../src/plugins/visualizations/public';
import { RegionMapExpressionFunctionDefinition } from './region_map_fn';
import { RegionMapVisParams } from './types';
import { extractLayerDescriptorParams } from './utils';

export const toExpressionAst: VisToExpressionAst<RegionMapVisParams> = (vis) => {
const regionMap = buildExpressionFunction<RegionMapExpressionFunctionDefinition>('regionmap', {
visConfig: JSON.stringify({
...vis.params,
mapCenter: vis.uiState.get('mapCenter', [0, 0]),
mapZoom: parseInt(vis.uiState.get('mapZoom', 2), 10),
layerDescriptorParams: extractLayerDescriptorParams(vis),
}),
});

const ast = buildExpression([regionMap]);

return ast.toAst();
};
Loading

0 comments on commit fabb710

Please sign in to comment.