[maps] reduce bundle size (#154789)

Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
Nathan Reese 2023-04-18 06:36:58 -06:00 committed by GitHub
parent 7235345601
commit dec97d4129
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
53 changed files with 679 additions and 714 deletions

View file

@ -15,6 +15,10 @@ export const APP_ICON_SOLUTION = 'logoKibana';
export const APP_NAME = i18n.translate('xpack.maps.visTypeAlias.title', {
defaultMessage: 'Maps',
});
export const MAP_EMBEDDABLE_NAME = i18n.translate('xpack.maps.embeddableDisplayName', {
defaultMessage: 'map',
});
export const INITIAL_LAYERS_KEY = 'initialLayers';
export const MAPS_APP_PATH = `app/${APP_ID}`;
@ -36,6 +40,8 @@ export const OPEN_LAYER_WIZARD = 'openLayerWizard';
// Centroids are a single point for representing lines, multiLines, polygons, and multiPolygons
export const KBN_IS_CENTROID_FEATURE = '__kbn_is_centroid_feature__';
export const GEOJSON_FEATURE_ID_PROPERTY_NAME = '__kbn__feature_id__';
export function getNewMapPath() {
return `/${MAPS_APP_PATH}/${MAP_PATH}`;
}

View file

@ -9,18 +9,6 @@ import { i18n } from '@kbn/i18n';
import { ES_SPATIAL_RELATIONS } from './constants';
export function getAppTitle() {
return i18n.translate('xpack.maps.appTitle', {
defaultMessage: 'Maps',
});
}
export function getMapEmbeddableDisplayName() {
return i18n.translate('xpack.maps.embeddableDisplayName', {
defaultMessage: 'map',
});
}
export function getDataSourceLabel() {
return i18n.translate('xpack.maps.source.dataSourceLabel', {
defaultMessage: 'Data source',

View file

@ -5,8 +5,7 @@
* 2.0.
*/
import { LayerDescriptor } from '../../common/descriptor_types';
import { lazyLoadMapModules } from '../lazy_load_bundle';
import type { LayerDescriptor } from '../../common/descriptor_types';
import type { CreateLayerDescriptorParams } from '../classes/sources/es_search_source';
export const createLayerDescriptors = {
@ -14,17 +13,21 @@ export const createLayerDescriptors = {
indexPatternId: string,
indexPatternTitle: string
): Promise<LayerDescriptor[]> {
const mapModules = await lazyLoadMapModules();
return mapModules.createSecurityLayerDescriptors(indexPatternId, indexPatternTitle);
const { createSecurityLayerDescriptors } = await import(
'../classes/layers/wizards/solution_layers/security'
);
return createSecurityLayerDescriptors(indexPatternId, indexPatternTitle);
},
async createBasemapLayerDescriptor(): Promise<LayerDescriptor | null> {
const mapModules = await lazyLoadMapModules();
return mapModules.createBasemapLayerDescriptor();
const { createBasemapLayerDescriptor } = await import(
'../classes/layers/create_basemap_layer_descriptor'
);
return createBasemapLayerDescriptor();
},
async createESSearchSourceLayerDescriptor(
params: CreateLayerDescriptorParams
): Promise<LayerDescriptor> {
const mapModules = await lazyLoadMapModules();
return mapModules.createESSearchSourceLayerDescriptor(params);
const { createLayerDescriptor } = await import('../classes/sources/es_search_source');
return createLayerDescriptor(params);
},
};

View file

@ -5,12 +5,11 @@
* 2.0.
*/
import { EMSTermJoinConfig, SampleValuesConfig } from '../ems_autosuggest';
import { lazyLoadMapModules } from '../lazy_load_bundle';
import type { EMSTermJoinConfig, SampleValuesConfig } from '../ems_autosuggest';
export async function suggestEMSTermJoinConfig(
sampleValuesConfig: SampleValuesConfig
): Promise<EMSTermJoinConfig | null> {
const mapModules = await lazyLoadMapModules();
return await mapModules.suggestEMSTermJoinConfig(sampleValuesConfig);
const { suggestEMSTermJoinConfig: suggestEms } = await import('../ems_autosuggest');
return await suggestEms(sampleValuesConfig);
}

View file

@ -6,4 +6,4 @@
*/
export type { LayerWizard, LayerWizardWithMeta, RenderWizardArguments } from './wizards';
export { getLayerWizards, registerLayerWizardExternal } from './wizards';
export { getLayerWizards } from './wizards';

View file

@ -5,7 +5,8 @@
* 2.0.
*/
import { assignFeatureIds, GEOJSON_FEATURE_ID_PROPERTY_NAME } from './assign_feature_ids';
import { GEOJSON_FEATURE_ID_PROPERTY_NAME } from '../../../../../common/constants';
import { assignFeatureIds } from './assign_feature_ids';
import { FeatureCollection, Feature, Point } from 'geojson';
const featureId = 'myFeature1';

View file

@ -6,9 +6,8 @@
*/
import _ from 'lodash';
import { FeatureCollection, Feature } from 'geojson';
export const GEOJSON_FEATURE_ID_PROPERTY_NAME = '__kbn__feature_id__';
import type { FeatureCollection, Feature } from 'geojson';
import { GEOJSON_FEATURE_ID_PROPERTY_NAME } from '../../../../../common/constants';
let idCounter = 0;

View file

@ -14,6 +14,7 @@ import type { FilterSpecification, Map as MbMap, GeoJSONSource } from '@kbn/mapb
import {
EMPTY_FEATURE_COLLECTION,
FEATURE_VISIBLE_PROPERTY_NAME,
GEOJSON_FEATURE_ID_PROPERTY_NAME,
LAYER_TYPE,
SOURCE_BOUNDS_DATA_REQUEST_ID,
} from '../../../../../common/constants';
@ -35,7 +36,6 @@ import {
} from '../vector_layer';
import { DataRequestAbortError } from '../../../util/data_request';
import { getFeatureCollectionBounds } from '../../../util/get_feature_collection_bounds';
import { GEOJSON_FEATURE_ID_PROPERTY_NAME } from './assign_feature_ids';
import { syncGeojsonSourceData } from './geojson_source_data';
import { performInnerJoins } from './perform_inner_joins';
import { pluckStyleMetaFromFeatures } from './pluck_style_meta_from_features';

View file

@ -10,4 +10,4 @@ export type {
LayerWizardWithMeta,
RenderWizardArguments,
} from './layer_wizard_registry';
export { getLayerWizards, registerLayerWizardExternal } from './layer_wizard_registry';
export { getLayerWizards } from './layer_wizard_registry';

View file

@ -5,6 +5,7 @@
* 2.0.
*/
import '../../_index.scss';
import React, { Component } from 'react';
import classNames from 'classnames';
import { EuiFlexGroup, EuiFlexItem, EuiCallOut } from '@elastic/eui';

View file

@ -8,25 +8,19 @@
import React, { Component, RefObject } from 'react';
import { first } from 'rxjs/operators';
import { v4 as uuidv4 } from 'uuid';
import { EuiLoadingChart } from '@elastic/eui';
import type { Filter } from '@kbn/es-query';
import type { Query, TimeRange } from '@kbn/es-query';
import type { LayerDescriptor, MapCenterAndZoom } from '../../common/descriptor_types';
import type { MapEmbeddableType } from './types';
import type { LazyLoadedMapModules } from '../lazy_load_bundle';
import { lazyLoadMapModules } from '../lazy_load_bundle';
import { MapEmbeddable } from './map_embeddable';
import { createBasemapLayerDescriptor } from '../classes/layers/create_basemap_layer_descriptor';
interface Props {
title: string;
filters?: Filter[];
query?: Query;
timeRange?: TimeRange;
getLayerDescriptors: (
mapModules: Pick<
LazyLoadedMapModules,
'createTileMapLayerDescriptor' | 'createRegionMapLayerDescriptor'
>
) => LayerDescriptor[];
getLayerDescriptors: () => LayerDescriptor[];
mapCenter?: MapCenterAndZoom;
onInitialRenderComplete?: () => void;
/*
@ -35,48 +29,13 @@ interface Props {
isSharable?: boolean;
}
interface State {
isLoaded: boolean;
}
export class MapComponent extends Component<Props, State> {
private _isMounted = false;
private _mapEmbeddable?: MapEmbeddableType | undefined;
export class MapComponent extends Component<Props> {
private _mapEmbeddable: MapEmbeddableType;
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(
constructor(props: Props) {
super(props);
this._mapEmbeddable = new MapEmbeddable(
{
editable: false,
},
@ -85,11 +44,8 @@ export class MapComponent extends Component<Props, State> {
attributes: {
title: this.props.title,
layerListJSON: JSON.stringify([
mapModules.createBasemapLayerDescriptor(),
...this.props.getLayerDescriptors({
createRegionMapLayerDescriptor: mapModules.createRegionMapLayerDescriptor,
createTileMapLayerDescriptor: mapModules.createTileMapLayerDescriptor,
}),
createBasemapLayerDescriptor(),
...this.props.getLayerDescriptors(),
]),
},
mapCenter: this.props.mapCenter,
@ -101,7 +57,7 @@ export class MapComponent extends Component<Props, State> {
.getOnRenderComplete$()
.pipe(first())
.subscribe(() => {
if (this._isMounted && this.props.onInitialRenderComplete) {
if (this.props.onInitialRenderComplete) {
this.props.onInitialRenderComplete();
}
});
@ -110,16 +66,27 @@ export class MapComponent extends Component<Props, State> {
if (this.props.isSharable !== undefined) {
this._mapEmbeddable.setIsSharable(this.props.isSharable);
}
}
componentDidMount() {
if (this._embeddableRef.current) {
this._mapEmbeddable.render(this._embeddableRef.current);
}
}
render() {
if (!this.state.isLoaded) {
return <EuiLoadingChart mono size="l" />;
}
componentWillUnmount() {
this._mapEmbeddable.destroy();
}
componentDidUpdate() {
this._mapEmbeddable.updateInput({
filters: this.props.filters,
query: this.props.query,
timeRange: this.props.timeRange,
});
}
render() {
return <div className="mapEmbeddableContainer" ref={this._embeddableRef} />;
}
}

View file

@ -21,6 +21,7 @@ import {
startWith,
} from 'rxjs/operators';
import { Unsubscribe } from 'redux';
import type { PaletteRegistry } from '@kbn/coloring';
import type { KibanaExecutionContext } from '@kbn/core/public';
import { EuiEmptyPrompt } from '@elastic/eui';
import { type Filter } from '@kbn/es-query';
@ -82,7 +83,7 @@ import {
} from '../../common/constants';
import { RenderToolTipContent } from '../classes/tooltips/tooltip_property';
import {
getChartsPaletteServiceGetColor,
getCharts,
getCoreI18n,
getExecutionContextService,
getHttp,
@ -109,6 +110,24 @@ import {
MapEmbeddableOutput,
} from './types';
async function getChartsPaletteServiceGetColor(): Promise<((value: string) => string) | null> {
const chartsService = getCharts();
const paletteRegistry: PaletteRegistry | null = chartsService
? await chartsService.palettes.getPalettes()
: null;
if (!paletteRegistry) {
return null;
}
const paletteDefinition = paletteRegistry.get('default');
const chartConfiguration = { syncColors: true };
return (value: string) => {
const series = [{ name: value, rankAtDepth: 0, totalSeriesAtDepth: 1 }];
const color = paletteDefinition.getCategoricalColor(series, chartConfiguration);
return color ? color : '#3d3d3d';
};
}
function getIsRestore(searchSessionId?: string) {
if (!searchSessionId) {
return false;

View file

@ -8,12 +8,10 @@
import { first } from 'rxjs/operators';
import { i18n } from '@kbn/i18n';
import { EmbeddableFactoryDefinition, IContainer } from '@kbn/embeddable-plugin/public';
import { MAP_SAVED_OBJECT_TYPE, APP_ICON } from '../../common/constants';
import { getMapEmbeddableDisplayName } from '../../common/i18n_getters';
import { MAP_SAVED_OBJECT_TYPE, APP_ICON, MAP_EMBEDDABLE_NAME } from '../../common/constants';
import { extract, inject } from '../../common/embeddable';
import { MapByReferenceInput, MapEmbeddableInput } from './types';
import { lazyLoadMapModules } from '../lazy_load_bundle';
import { getApplication, getUsageCollection } from '../kibana_services';
import { getApplication, getMapsCapabilities, getUsageCollection } from '../kibana_services';
export class MapEmbeddableFactory implements EmbeddableFactoryDefinition {
type = MAP_SAVED_OBJECT_TYPE;
@ -26,7 +24,6 @@ export class MapEmbeddableFactory implements EmbeddableFactoryDefinition {
};
async isEditable() {
const { getMapsCapabilities } = await lazyLoadMapModules();
return getMapsCapabilities().save as boolean;
}
@ -36,7 +33,7 @@ export class MapEmbeddableFactory implements EmbeddableFactoryDefinition {
}
getDisplayName() {
return getMapEmbeddableDisplayName();
return MAP_EMBEDDABLE_NAME;
}
createFromSavedObject = async (
@ -51,7 +48,7 @@ export class MapEmbeddableFactory implements EmbeddableFactoryDefinition {
};
create = async (input: MapEmbeddableInput, parent?: IContainer) => {
const { MapEmbeddable } = await lazyLoadMapModules();
const { MapEmbeddable } = await import('./map_embeddable');
const usageCollection = getUsageCollection();
if (usageCollection) {
// currentAppId$ is a BehaviorSubject exposed as an observable so subscription gets last value upon subscribe

View file

@ -7,12 +7,11 @@
import { i18n } from '@kbn/i18n';
import type { FeatureCatalogueCategory } from '@kbn/home-plugin/public';
import { APP_ID, APP_ICON } from '../common/constants';
import { getAppTitle } from '../common/i18n_getters';
import { APP_ID, APP_ICON, APP_NAME } from '../common/constants';
export const featureCatalogueEntry = {
id: APP_ID,
title: getAppTitle(),
title: APP_NAME,
subtitle: i18n.translate('xpack.maps.featureCatalogue.mapsSubtitle', {
defaultMessage: 'Plot geographic data.',
}),

View file

@ -17,12 +17,10 @@ export const plugin: PluginInitializer<MapsPluginSetup, MapsPluginStart> = (
return new MapsPlugin(initContext);
};
export { MAP_SAVED_OBJECT_TYPE } from '../common/constants';
export { MAPS_APP_LOCATOR } from './locators';
export { GEOJSON_FEATURE_ID_PROPERTY_NAME, MAP_SAVED_OBJECT_TYPE } from '../common/constants';
export { MAPS_APP_LOCATOR } from './locators/map_locator/locator_definition';
export type { PreIndexedShape } from '../common/elasticsearch_util';
export { GEOJSON_FEATURE_ID_PROPERTY_NAME } from './classes/layers/vector_layer/geojson_vector_layer/assign_feature_ids';
export type {
ITooltipProperty,
RenderTooltipContentParams,

View file

@ -6,7 +6,6 @@
*/
import type { CoreStart } from '@kbn/core/public';
import type { PaletteRegistry } from '@kbn/coloring';
import type { EMSSettings } from '@kbn/maps-ems-plugin/common/ems_settings';
import { MapsEmsPluginPublicStart } from '@kbn/maps-ems-plugin/public';
import type { MapsConfigType } from '../config';
@ -50,6 +49,7 @@ export const getMapsCapabilities = () => coreStart.application.capabilities.maps
export const getVisualizeCapabilities = () => coreStart.application.capabilities.visualize;
export const getDocLinks = () => coreStart.docLinks;
export const getCoreOverlays = () => coreStart.overlays;
export const getCharts = () => pluginsStart.charts;
export const getData = () => pluginsStart.data;
export const getUiActions = () => pluginsStart.uiActions;
export const getCore = () => coreStart;
@ -90,34 +90,7 @@ export const getEMSSettings: () => EMSSettings = () => {
export const getEmsTileLayerId = () => mapsEms.config.emsTileLayerId;
export const getTilemap = () => {
if (mapsEms.config.tilemap) {
return mapsEms.config.tilemap;
} else {
return {};
}
};
export const getShareService = () => pluginsStart.share;
export const getIsAllowByValueEmbeddables = () =>
pluginsStart.dashboard.dashboardFeatureFlagConfig.allowByValueEmbeddables;
export async function getChartsPaletteServiceGetColor(): Promise<
((value: string) => string) | null
> {
const paletteRegistry: PaletteRegistry | null = pluginsStart.charts
? await pluginsStart.charts.palettes.getPalettes()
: null;
if (!paletteRegistry) {
return null;
}
const paletteDefinition = paletteRegistry.get('default');
const chartConfiguration = { syncColors: true };
return (value: string) => {
const series = [{ name: value, rankAtDepth: 0, totalSeriesAtDepth: 1 }];
const color = paletteDefinition.getCategoricalColor(series, chartConfiguration);
return color ? color : '#3d3d3d';
};
}

View file

@ -1,106 +0,0 @@
/*
* 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 { DataViewsContract } from '@kbn/data-views-plugin/common';
import { AppMountParameters, CoreStart } from '@kbn/core/public';
import { IContainer } from '@kbn/embeddable-plugin/public';
import type { SavedObjectTaggingPluginStart } from '@kbn/saved-objects-tagging-plugin/public';
import { LayerDescriptor } from '../../common/descriptor_types';
import type {
MapEmbeddableConfig,
MapEmbeddableInput,
MapEmbeddableType,
} from '../embeddable/types';
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>;
export interface LazyLoadedMapModules {
MapEmbeddable: new (
config: MapEmbeddableConfig,
initialInput: MapEmbeddableInput,
parent?: IContainer
) => MapEmbeddableType;
getIndexPatternService: () => DataViewsContract;
getMapsCapabilities: () => any;
renderApp: (
params: AppMountParameters,
deps: {
coreStart: CoreStart;
AppUsageTracker: React.FC;
savedObjectsTagging?: SavedObjectTaggingPluginStart;
}
) => Promise<() => void>;
createSecurityLayerDescriptors: (
indexPatternId: string,
indexPatternTitle: string
) => LayerDescriptor[];
createTileMapLayerDescriptor: ({
label,
mapType,
colorSchema,
indexPatternId,
geoFieldName,
metricAgg,
metricFieldName,
}: CreateTileMapLayerDescriptorParams) => LayerDescriptor | null;
createRegionMapLayerDescriptor: ({
label,
emsLayerId,
leftFieldName,
termsFieldName,
termsSize,
colorSchema,
indexPatternId,
metricAgg,
metricFieldName,
}: CreateRegionMapLayerDescriptorParams) => LayerDescriptor | null;
createBasemapLayerDescriptor: () => LayerDescriptor | null;
createESSearchSourceLayerDescriptor: (params: CreateLayerDescriptorParams) => LayerDescriptor;
suggestEMSTermJoinConfig: (config: SampleValuesConfig) => Promise<EMSTermJoinConfig | null>;
}
export async function lazyLoadMapModules(): Promise<LazyLoadedMapModules> {
if (typeof loadModulesPromise !== 'undefined') {
return loadModulesPromise;
}
loadModulesPromise = new Promise(async (resolve, reject) => {
try {
const {
MapEmbeddable,
getIndexPatternService,
getMapsCapabilities,
renderApp,
createSecurityLayerDescriptors,
createTileMapLayerDescriptor,
createRegionMapLayerDescriptor,
createBasemapLayerDescriptor,
createESSearchSourceLayerDescriptor,
suggestEMSTermJoinConfig,
} = await import('./lazy');
resolve({
MapEmbeddable,
getIndexPatternService,
getMapsCapabilities,
renderApp,
createSecurityLayerDescriptors,
createTileMapLayerDescriptor,
createRegionMapLayerDescriptor,
createBasemapLayerDescriptor,
createESSearchSourceLayerDescriptor,
suggestEMSTermJoinConfig,
});
} catch (error) {
reject(error);
}
});
return loadModulesPromise;
}

View file

@ -1,17 +0,0 @@
/*
* 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 '../../_index.scss';
export * from '../../embeddable/map_embeddable';
export * from '../../kibana_services';
export { renderApp } from '../../render_app';
export * from '../../classes/layers/wizards/solution_layers/security';
export { createTileMapLayerDescriptor } from '../../classes/layers/create_tile_map_layer_descriptor';
export { createRegionMapLayerDescriptor } from '../../classes/layers/create_region_map_layer_descriptor';
export { createBasemapLayerDescriptor } from '../../classes/layers/create_basemap_layer_descriptor';
export { createLayerDescriptor as createESSearchSourceLayerDescriptor } from '../../classes/sources/es_search_source';
export { suggestEMSTermJoinConfig } from '../../ems_autosuggest';

View file

@ -8,4 +8,3 @@
export { GEOHASH_GRID, getGeoHashBucketAgg } from './tile_map';
export { createRegionMapFn, regionMapRenderer, regionMapVisType } from './region_map';
export { createTileMapFn, tileMapRenderer, tileMapVisType } from './tile_map';
export { isLegacyMap } from './is_legacy_map';

View file

@ -9,8 +9,8 @@ import React from 'react';
import type { Filter } from '@kbn/es-query';
import type { Query, TimeRange } from '@kbn/es-query';
import { RegionMapVisConfig } from './types';
import type { LazyLoadedMapModules } from '../../lazy_load_bundle';
import { MapComponent } from '../../embeddable/map_component';
import { createRegionMapLayerDescriptor } from '../../classes/layers/create_region_map_layer_descriptor';
interface Props {
filters?: Filter[];
@ -26,11 +26,7 @@ function RegionMapVisualization(props: Props) {
lon: props.visConfig.mapCenter[1],
zoom: props.visConfig.mapZoom,
};
function getLayerDescriptors({
createRegionMapLayerDescriptor,
}: {
createRegionMapLayerDescriptor: LazyLoadedMapModules['createRegionMapLayerDescriptor'];
}) {
function getLayerDescriptors() {
const layerDescriptor = createRegionMapLayerDescriptor(props.visConfig.layerDescriptorParams);
return layerDescriptor ? [layerDescriptor] : [];
}

View file

@ -8,9 +8,9 @@
import React from 'react';
import type { Filter } from '@kbn/es-query';
import type { Query, TimeRange } from '@kbn/es-query';
import { TileMapVisConfig } from './types';
import type { LazyLoadedMapModules } from '../../lazy_load_bundle';
import type { TileMapVisConfig } from './types';
import { MapComponent } from '../../embeddable/map_component';
import { createTileMapLayerDescriptor } from '../../classes/layers/create_tile_map_layer_descriptor';
interface Props {
filters?: Filter[];
@ -26,11 +26,7 @@ function TileMapVisualization(props: Props) {
lon: props.visConfig.mapCenter[1],
zoom: props.visConfig.mapZoom,
};
function getLayerDescriptors({
createTileMapLayerDescriptor,
}: {
createTileMapLayerDescriptor: LazyLoadedMapModules['createTileMapLayerDescriptor'];
}) {
function getLayerDescriptors() {
const layerDescriptor = createTileMapLayerDescriptor(props.visConfig.layerDescriptorParams);
return layerDescriptor ? [layerDescriptor] : [];
}

View file

@ -1,257 +0,0 @@
/*
* 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.
*/
/* eslint-disable max-classes-per-file */
import rison from '@kbn/rison';
import type { DataViewSpec } from '@kbn/data-views-plugin/public';
import type { SerializableRecord } from '@kbn/utility-types';
import { type Filter, isFilterPinned, type TimeRange, type Query } from '@kbn/es-query';
import type { GlobalQueryStateFromUrl, RefreshInterval } from '@kbn/data-plugin/public';
import { setStateToKbnUrl } from '@kbn/kibana-utils-plugin/public';
import type { LocatorDefinition, LocatorPublic } from '@kbn/share-plugin/public';
import type { LayerDescriptor } from '../common/descriptor_types';
import { INITIAL_LAYERS_KEY, APP_ID } from '../common/constants';
import { lazyLoadMapModules } from './lazy_load_bundle';
export interface MapsAppLocatorParams extends SerializableRecord {
/**
* If given, it will load the given map else will load the create a new map page.
*/
mapId?: string;
/**
* Optionally set the time range in the time picker.
*/
timeRange?: TimeRange;
/**
* Optionally set the initial Layers.
*/
initialLayers?: LayerDescriptor[] & SerializableRecord;
/**
* Optionally set the refresh interval.
*/
refreshInterval?: RefreshInterval & SerializableRecord;
/**
* Optionally apply filers. NOTE: if given and used in conjunction with `mapId`, and the
* saved map has filters saved with it, this will _replace_ those filters.
*/
filters?: Filter[];
/**
* Optionally set a query. NOTE: if given and used in conjunction with `mapId`, and the
* saved map has a query saved with it, this will _replace_ that query.
*/
query?: Query;
/**
* If not given, will use the uiSettings configuration for `storeInSessionStorage`. useHash determines
* whether to hash the data in the url to avoid url length issues.
*/
hash?: boolean;
/**
* Optionally pass adhoc data view spec.
*/
dataViewSpec?: DataViewSpec;
}
export const MAPS_APP_LOCATOR = 'MAPS_APP_LOCATOR' as const;
export type MapsAppLocator = LocatorPublic<MapsAppLocatorParams>;
export interface MapsAppLocatorDependencies {
useHash: boolean;
}
export class MapsAppLocatorDefinition implements LocatorDefinition<MapsAppLocatorParams> {
public readonly id = MAPS_APP_LOCATOR;
constructor(protected readonly deps: MapsAppLocatorDependencies) {}
public readonly getLocation = async (params: MapsAppLocatorParams) => {
const { mapId, filters, query, refreshInterval, timeRange, initialLayers, hash } = params;
const useHash = hash ?? this.deps.useHash;
const appState: {
query?: Query;
filters?: Filter[];
vis?: unknown;
} = {};
const queryState: GlobalQueryStateFromUrl = {};
if (query) appState.query = query;
if (filters && filters.length) appState.filters = filters?.filter((f) => !isFilterPinned(f));
if (timeRange) queryState.time = timeRange;
if (filters && filters.length) queryState.filters = filters?.filter((f) => isFilterPinned(f));
if (refreshInterval) queryState.refreshInterval = refreshInterval;
let path = `/map#/${mapId || ''}`;
path = setStateToKbnUrl<GlobalQueryStateFromUrl>('_g', queryState, { useHash }, path);
path = setStateToKbnUrl('_a', appState, { useHash }, path);
if (initialLayers && initialLayers.length) {
const risonEncodedInitialLayers = rison.encodeArray(initialLayers);
path = `${path}&${INITIAL_LAYERS_KEY}=${encodeURIComponent(risonEncodedInitialLayers)}`;
}
return {
app: APP_ID,
path,
state: params.dataViewSpec
? {
dataViewSpec: params.dataViewSpec,
}
: {},
};
};
}
export interface MapsAppTileMapLocatorParams extends SerializableRecord {
label: string;
mapType: string;
colorSchema: string;
indexPatternId?: string;
geoFieldName?: string;
metricAgg: string;
metricFieldName?: string;
timeRange?: TimeRange;
filters?: Filter[];
query?: Query;
hash?: boolean;
}
export type MapsAppTileMapLocator = LocatorPublic<MapsAppLocatorParams>;
export const MAPS_APP_TILE_MAP_LOCATOR = 'MAPS_APP_TILE_MAP_LOCATOR' as const;
export interface MapsAppTileMapLocatorDependencies {
locator: MapsAppLocator;
}
export class MapsAppTileMapLocatorDefinition
implements LocatorDefinition<MapsAppTileMapLocatorParams>
{
public readonly id = MAPS_APP_TILE_MAP_LOCATOR;
constructor(protected readonly deps: MapsAppTileMapLocatorDependencies) {}
public readonly getLocation = async (params: MapsAppTileMapLocatorParams) => {
const {
label,
mapType,
colorSchema,
indexPatternId,
geoFieldName,
metricAgg,
metricFieldName,
filters,
query,
timeRange,
hash = true,
} = params;
const mapModules = await lazyLoadMapModules();
const initialLayers = [] as unknown as LayerDescriptor[] & SerializableRecord;
const tileMapLayerDescriptor = mapModules.createTileMapLayerDescriptor({
label,
mapType,
colorSchema,
indexPatternId,
geoFieldName,
metricAgg,
metricFieldName,
});
if (tileMapLayerDescriptor) {
initialLayers.push(tileMapLayerDescriptor);
}
return await this.deps.locator.getLocation({
initialLayers,
filters,
query,
timeRange,
hash,
});
};
}
export interface MapsAppRegionMapLocatorParams extends SerializableRecord {
label: string;
emsLayerId?: string;
leftFieldName?: string;
termsFieldName?: string;
termsSize?: number;
colorSchema: string;
indexPatternId?: string;
metricAgg: string;
metricFieldName?: string;
timeRange?: TimeRange;
filters?: Filter[];
query?: Query;
hash?: boolean;
}
export type MapsAppRegionMapLocator = LocatorPublic<MapsAppRegionMapLocatorParams>;
export const MAPS_APP_REGION_MAP_LOCATOR = 'MAPS_APP_REGION_MAP_LOCATOR' as const;
export interface MapsAppRegionMapLocatorDependencies {
locator: MapsAppLocator;
}
export class MapsAppRegionMapLocatorDefinition
implements LocatorDefinition<MapsAppRegionMapLocatorParams>
{
public readonly id = MAPS_APP_REGION_MAP_LOCATOR;
constructor(protected readonly deps: MapsAppRegionMapLocatorDependencies) {}
public readonly getLocation = async (params: MapsAppRegionMapLocatorParams) => {
const {
label,
emsLayerId,
leftFieldName,
termsFieldName,
termsSize,
colorSchema,
indexPatternId,
metricAgg,
metricFieldName,
filters,
query,
timeRange,
hash = true,
} = params;
const mapModules = await lazyLoadMapModules();
const initialLayers = [] as unknown as LayerDescriptor[] & SerializableRecord;
const regionMapLayerDescriptor = mapModules.createRegionMapLayerDescriptor({
label,
emsLayerId,
leftFieldName,
termsFieldName,
termsSize,
colorSchema,
indexPatternId,
metricAgg,
metricFieldName,
});
if (regionMapLayerDescriptor) {
initialLayers.push(regionMapLayerDescriptor);
}
return await this.deps.locator.getLocation({
initialLayers,
filters,
query,
timeRange,
hash,
});
};
}

View file

@ -0,0 +1,50 @@
/*
* 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 rison from '@kbn/rison';
import { setStateToKbnUrl } from '@kbn/kibana-utils-plugin/public';
import type { GlobalQueryStateFromUrl } from '@kbn/data-plugin/public';
import type { Filter, Query } from '@kbn/es-query';
import { isFilterPinned } from '@kbn/es-query';
import { INITIAL_LAYERS_KEY, APP_ID } from '../../../common/constants';
import type { MapsAppLocatorDependencies, MapsAppLocatorParams } from './types';
export function getLocation(params: MapsAppLocatorParams, deps: MapsAppLocatorDependencies) {
const { mapId, filters, query, refreshInterval, timeRange, initialLayers, hash } = params;
const useHash = hash ?? deps.useHash;
const appState: {
query?: Query;
filters?: Filter[];
vis?: unknown;
} = {};
const queryState: GlobalQueryStateFromUrl = {};
if (query) appState.query = query;
if (filters && filters.length) appState.filters = filters?.filter((f) => !isFilterPinned(f));
if (timeRange) queryState.time = timeRange;
if (filters && filters.length) queryState.filters = filters?.filter((f) => isFilterPinned(f));
if (refreshInterval) queryState.refreshInterval = refreshInterval;
let path = `/map#/${mapId || ''}`;
path = setStateToKbnUrl<GlobalQueryStateFromUrl>('_g', queryState, { useHash }, path);
path = setStateToKbnUrl('_a', appState, { useHash }, path);
if (initialLayers && initialLayers.length) {
const risonEncodedInitialLayers = rison.encodeArray(initialLayers);
path = `${path}&${INITIAL_LAYERS_KEY}=${encodeURIComponent(risonEncodedInitialLayers)}`;
}
return {
app: APP_ID,
path,
state: params.dataViewSpec
? {
dataViewSpec: params.dataViewSpec,
}
: {},
};
}

View file

@ -5,11 +5,11 @@
* 2.0.
*/
import { LAYER_TYPE, SOURCE_TYPES, SCALING_TYPES } from '../common/constants';
import { LAYER_TYPE, SOURCE_TYPES, SCALING_TYPES } from '../../../common/constants';
import { FilterStateStore } from '@kbn/es-query';
import { MapsAppLocatorDefinition } from './locators';
import { SerializableRecord } from '@kbn/utility-types';
import { LayerDescriptor } from '../common/descriptor_types';
import { MapsAppLocatorDefinition } from './locator_definition';
import type { SerializableRecord } from '@kbn/utility-types';
import type { LayerDescriptor } from '../../../common/descriptor_types';
const MAP_ID: string = '2c9c1f60-1909-11e9-919b-ffe5949a18d2';
const LAYER_ID: string = '13823000-99b9-11ea-9eb6-d9e8adceb647';

View file

@ -0,0 +1,22 @@
/*
* 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 type { LocatorDefinition } from '@kbn/share-plugin/public';
import type { MapsAppLocatorDependencies, MapsAppLocatorParams } from './types';
export const MAPS_APP_LOCATOR = 'MAPS_APP_LOCATOR' as const;
export class MapsAppLocatorDefinition implements LocatorDefinition<MapsAppLocatorParams> {
public readonly id = MAPS_APP_LOCATOR;
constructor(protected readonly deps: MapsAppLocatorDependencies) {}
public readonly getLocation = async (params: MapsAppLocatorParams) => {
const { getLocation } = await import('./get_location');
return getLocation(params, this.deps);
};
}

View file

@ -0,0 +1,64 @@
/*
* 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 type { SerializableRecord } from '@kbn/utility-types';
import type { Filter, TimeRange, Query } from '@kbn/es-query';
import type { DataViewSpec } from '@kbn/data-views-plugin/public';
import type { RefreshInterval } from '@kbn/data-plugin/public';
import type { LocatorPublic } from '@kbn/share-plugin/public';
import type { LayerDescriptor } from '../../../common/descriptor_types';
export interface MapsAppLocatorParams extends SerializableRecord {
/**
* If given, it will load the given map else will load the create a new map page.
*/
mapId?: string;
/**
* Optionally set the time range in the time picker.
*/
timeRange?: TimeRange;
/**
* Optionally set the initial Layers.
*/
initialLayers?: LayerDescriptor[] & SerializableRecord;
/**
* Optionally set the refresh interval.
*/
refreshInterval?: RefreshInterval & SerializableRecord;
/**
* Optionally apply filers. NOTE: if given and used in conjunction with `mapId`, and the
* saved map has filters saved with it, this will _replace_ those filters.
*/
filters?: Filter[];
/**
* Optionally set a query. NOTE: if given and used in conjunction with `mapId`, and the
* saved map has a query saved with it, this will _replace_ that query.
*/
query?: Query;
/**
* If not given, will use the uiSettings configuration for `storeInSessionStorage`. useHash determines
* whether to hash the data in the url to avoid url length issues.
*/
hash?: boolean;
/**
* Optionally pass adhoc data view spec.
*/
dataViewSpec?: DataViewSpec;
}
export type MapsAppLocator = LocatorPublic<MapsAppLocatorParams>;
export interface MapsAppLocatorDependencies {
useHash: boolean;
}

View file

@ -0,0 +1,55 @@
/*
* 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 type { SerializableRecord } from '@kbn/utility-types';
import type { LayerDescriptor } from '../../../common/descriptor_types';
import { createRegionMapLayerDescriptor } from '../../classes/layers/create_region_map_layer_descriptor';
import type { MapsAppRegionMapLocatorParams, MapsAppRegionMapLocatorDependencies } from './types';
export async function getLocation(
params: MapsAppRegionMapLocatorParams,
deps: MapsAppRegionMapLocatorDependencies
) {
const {
label,
emsLayerId,
leftFieldName,
termsFieldName,
termsSize,
colorSchema,
indexPatternId,
metricAgg,
metricFieldName,
filters,
query,
timeRange,
hash = true,
} = params;
const initialLayers = [] as unknown as LayerDescriptor[] & SerializableRecord;
const regionMapLayerDescriptor = createRegionMapLayerDescriptor({
label,
emsLayerId,
leftFieldName,
termsFieldName,
termsSize,
colorSchema,
indexPatternId,
metricAgg,
metricFieldName,
});
if (regionMapLayerDescriptor) {
initialLayers.push(regionMapLayerDescriptor);
}
return await deps.locator.getLocation({
initialLayers,
filters,
query,
timeRange,
hash,
});
}

View file

@ -0,0 +1,24 @@
/*
* 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 type { LocatorDefinition } from '@kbn/share-plugin/public';
import type { MapsAppRegionMapLocatorParams, MapsAppRegionMapLocatorDependencies } from './types';
export const MAPS_APP_REGION_MAP_LOCATOR = 'MAPS_APP_REGION_MAP_LOCATOR' as const;
export class MapsAppRegionMapLocatorDefinition
implements LocatorDefinition<MapsAppRegionMapLocatorParams>
{
public readonly id = MAPS_APP_REGION_MAP_LOCATOR;
constructor(protected readonly deps: MapsAppRegionMapLocatorDependencies) {}
public readonly getLocation = async (params: MapsAppRegionMapLocatorParams) => {
const { getLocation } = await import('./get_location');
return getLocation(params, this.deps);
};
}

View file

@ -0,0 +1,33 @@
/*
* 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 type { SerializableRecord } from '@kbn/utility-types';
import type { Filter, TimeRange, Query } from '@kbn/es-query';
import type { LocatorPublic } from '@kbn/share-plugin/public';
import type { MapsAppLocator } from '../map_locator/types';
export interface MapsAppRegionMapLocatorParams extends SerializableRecord {
label: string;
emsLayerId?: string;
leftFieldName?: string;
termsFieldName?: string;
termsSize?: number;
colorSchema: string;
indexPatternId?: string;
metricAgg: string;
metricFieldName?: string;
timeRange?: TimeRange;
filters?: Filter[];
query?: Query;
hash?: boolean;
}
export type MapsAppRegionMapLocator = LocatorPublic<MapsAppRegionMapLocatorParams>;
export interface MapsAppRegionMapLocatorDependencies {
locator: MapsAppLocator;
}

View file

@ -0,0 +1,52 @@
/*
* 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 type { SerializableRecord } from '@kbn/utility-types';
import type { LayerDescriptor } from '../../../common/descriptor_types';
import { createTileMapLayerDescriptor } from '../../classes/layers/create_tile_map_layer_descriptor';
import type { MapsAppTileMapLocatorParams, MapsAppTileMapLocatorDependencies } from './types';
export async function getLocation(
params: MapsAppTileMapLocatorParams,
deps: MapsAppTileMapLocatorDependencies
) {
const {
label,
mapType,
colorSchema,
indexPatternId,
geoFieldName,
metricAgg,
metricFieldName,
filters,
query,
timeRange,
hash = true,
} = params;
const initialLayers = [] as unknown as LayerDescriptor[] & SerializableRecord;
const tileMapLayerDescriptor = createTileMapLayerDescriptor({
label,
mapType,
colorSchema,
indexPatternId,
geoFieldName,
metricAgg,
metricFieldName,
});
if (tileMapLayerDescriptor) {
initialLayers.push(tileMapLayerDescriptor);
}
return await deps.locator.getLocation({
initialLayers,
filters,
query,
timeRange,
hash,
});
}

View file

@ -0,0 +1,24 @@
/*
* 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 type { LocatorDefinition } from '@kbn/share-plugin/public';
import type { MapsAppTileMapLocatorParams, MapsAppTileMapLocatorDependencies } from './types';
export const MAPS_APP_TILE_MAP_LOCATOR = 'MAPS_APP_TILE_MAP_LOCATOR' as const;
export class MapsAppTileMapLocatorDefinition
implements LocatorDefinition<MapsAppTileMapLocatorParams>
{
public readonly id = MAPS_APP_TILE_MAP_LOCATOR;
constructor(protected readonly deps: MapsAppTileMapLocatorDependencies) {}
public readonly getLocation = async (params: MapsAppTileMapLocatorParams) => {
const { getLocation } = await import('./get_location');
return getLocation(params, this.deps);
};
}

View file

@ -0,0 +1,31 @@
/*
* 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 type { SerializableRecord } from '@kbn/utility-types';
import type { Filter, TimeRange, Query } from '@kbn/es-query';
import type { LocatorPublic } from '@kbn/share-plugin/public';
import type { MapsAppLocator } from '../map_locator/types';
export interface MapsAppTileMapLocatorParams extends SerializableRecord {
label: string;
mapType: string;
colorSchema: string;
indexPatternId?: string;
geoFieldName?: string;
metricAgg: string;
metricFieldName?: string;
timeRange?: TimeRange;
filters?: Filter[];
query?: Query;
hash?: boolean;
}
export type MapsAppTileMapLocator = LocatorPublic<MapsAppTileMapLocatorParams>;
export interface MapsAppTileMapLocatorDependencies {
locator: MapsAppLocator;
}

View file

@ -10,8 +10,7 @@ import type { ResolvedSimpleSavedObject } from '@kbn/core/public';
import { AttributeService } from '@kbn/embeddable-plugin/public';
import type { OnSaveProps } from '@kbn/saved-objects-plugin/public';
import type { MapAttributes } from '../common/content_management';
import { MAP_SAVED_OBJECT_TYPE } from '../common/constants';
import { getMapEmbeddableDisplayName } from '../common/i18n_getters';
import { MAP_EMBEDDABLE_NAME, MAP_SAVED_OBJECT_TYPE } from '../common/constants';
import { getCoreOverlays, getEmbeddableService } from './kibana_services';
import { extractReferences, injectReferences } from '../common/migrations/references';
import { mapsClient, checkForDuplicateTitle } from './content_management';
@ -108,7 +107,7 @@ export function getMapAttributeService(): MapAttributeService {
copyOnSave: false,
lastSavedTitle: '',
isTitleDuplicateConfirmed: props.isTitleDuplicateConfirmed,
getDisplayName: getMapEmbeddableDisplayName,
getDisplayName: () => MAP_EMBEDDABLE_NAME,
onTitleDuplicate: props.onTitleDuplicate,
},
{

View file

@ -56,32 +56,29 @@ import {
tileMapRenderer,
tileMapVisType,
} from './legacy_visualizations';
import {
MapsAppLocatorDefinition,
MapsAppRegionMapLocatorDefinition,
MapsAppTileMapLocatorDefinition,
} from './locators';
import { MapsAppLocatorDefinition } from './locators/map_locator/locator_definition';
import { MapsAppTileMapLocatorDefinition } from './locators/tile_map_locator/locator_definition';
import { MapsAppRegionMapLocatorDefinition } from './locators/region_map_locator/locator_definition';
import { registerLicensedFeatures, setLicensingPluginStart } from './licensed_features';
import { registerSource } from './classes/sources/source_registry';
import { registerLayerWizardExternal } from './classes/layers';
import { registerLayerWizardExternal } from './classes/layers/wizards/layer_wizard_registry';
import {
createLayerDescriptors,
MapsSetupApi,
MapsStartApi,
suggestEMSTermJoinConfig,
} from './api';
import { lazyLoadMapModules } from './lazy_load_bundle';
import { getAppTitle } from '../common/i18n_getters';
import { MapsXPackConfig, MapsConfigType } from '../config';
import { MapEmbeddableFactory } from './embeddable/map_embeddable_factory';
import { filterByMapExtentAction } from './trigger_actions/filter_by_map_extent_action';
import { synchronizeMovementAction } from './trigger_actions/synchronize_movement_action';
import { filterByMapExtentAction } from './trigger_actions/filter_by_map_extent/action';
import { synchronizeMovementAction } from './trigger_actions/synchronize_movement/action';
import { visualizeGeoFieldAction } from './trigger_actions/visualize_geo_field_action';
import { APP_ICON_SOLUTION, APP_ID, MAP_SAVED_OBJECT_TYPE } from '../common/constants';
import { APP_NAME, APP_ICON_SOLUTION, APP_ID, MAP_SAVED_OBJECT_TYPE } from '../common/constants';
import { getMapsVisTypeAlias } from './maps_vis_type_alias';
import { featureCatalogueEntry } from './feature_catalogue_entry';
import { setIsCloudEnabled, setMapAppConfig, setStartServices } from './kibana_services';
import { MapInspectorView, VectorTileInspectorView } from './inspector';
import { MapInspectorView } from './inspector/map_adapter/map_inspector_view';
import { VectorTileInspectorView } from './inspector/vector_tile_adapter/vector_tile_inspector_view';
import { setupLensChoroplethChart } from './lens';
import { CONTENT_ID, LATEST_VERSION } from '../common/content_management';
@ -193,7 +190,7 @@ export class MapsPlugin
core.application.register({
id: APP_ID,
title: getAppTitle(),
title: APP_NAME,
order: 4000,
icon: `plugins/${APP_ID}/icon.svg`,
euiIconType: APP_ICON_SOLUTION,
@ -202,7 +199,7 @@ export class MapsPlugin
const [coreStart, { savedObjectsTagging }] = await core.getStartServices();
const UsageTracker =
plugins.usageCollection?.components.ApplicationUsageTrackingProvider ?? React.Fragment;
const { renderApp } = await lazyLoadMapModules();
const { renderApp } = await import('./render_app');
return renderApp(params, { coreStart, AppUsageTracker: UsageTracker, savedObjectsTagging });
},
});
@ -212,7 +209,7 @@ export class MapsPlugin
version: {
latest: LATEST_VERSION,
},
name: getAppTitle(),
name: APP_NAME,
});
setupLensChoroplethChart(core, plugins.expressions, plugins.lens);

View file

@ -13,7 +13,7 @@ import { TableListView } from '@kbn/content-management-table-list';
import type { UserContentCommonSchema } from '@kbn/content-management-table-list';
import type { MapItem } from '../../../common/content_management';
import { APP_ID, getEditPath, MAP_PATH } from '../../../common/constants';
import { APP_ID, APP_NAME, getEditPath, MAP_PATH } from '../../../common/constants';
import {
getMapsCapabilities,
getCoreChrome,
@ -22,7 +22,6 @@ import {
getUiSettings,
getUsageCollection,
} from '../../kibana_services';
import { getAppTitle } from '../../../common/i18n_getters';
import { mapsClient } from '../../content_management';
const SAVED_OBJECTS_LIMIT_SETTING = 'savedObjects:listingLimit';
@ -73,8 +72,8 @@ function MapsListViewComp({ history }: Props) {
const listingLimit = getUiSettings().get(SAVED_OBJECTS_LIMIT_SETTING);
const initialPageSize = getUiSettings().get(SAVED_OBJECTS_PER_PAGE_SETTING);
getCoreChrome().docTitle.change(getAppTitle());
getCoreChrome().setBreadcrumbs([{ text: getAppTitle() }]);
getCoreChrome().docTitle.change(APP_NAME);
getCoreChrome().setBreadcrumbs([{ text: APP_NAME }]);
const findMaps = useCallback(
async (
@ -128,7 +127,7 @@ function MapsListViewComp({ history }: Props) {
entityNamePlural={i18n.translate('xpack.maps.mapListing.entityNamePlural', {
defaultMessage: 'maps',
})}
tableListTitle={getAppTitle()}
tableListTitle={APP_NAME}
onClickTitle={({ id }) => history.push(getEditPath(id))}
/>
);

View file

@ -47,8 +47,12 @@ import { AppStateManager, startAppStateSyncing } from '../url_state';
import { MapContainer } from '../../../connected_components/map_container';
import { getIndexPatternsFromIds } from '../../../index_pattern_util';
import { getTopNavConfig } from '../top_nav_config';
import { getEditPath, getFullPath, APP_ID } from '../../../../common/constants';
import { getMapEmbeddableDisplayName } from '../../../../common/i18n_getters';
import {
getEditPath,
getFullPath,
APP_ID,
MAP_EMBEDDABLE_NAME,
} from '../../../../common/constants';
import {
getInitialQuery,
getInitialRefreshConfig,
@ -432,7 +436,7 @@ export class MapApp extends React.Component<Props, State> {
await spaces.ui.redirectLegacyUrl({
path: newPath,
aliasPurpose: sharingSavedObjectProps.aliasPurpose,
objectNoun: getMapEmbeddableDisplayName(),
objectNoun: MAP_EMBEDDABLE_NAME,
});
return;
}
@ -547,7 +551,7 @@ export class MapApp extends React.Component<Props, State> {
const spaces = getSpacesApi();
return spaces && sharingSavedObjectProps?.outcome === 'conflict'
? spaces.ui.components.getLegacyUrlConflict({
objectNoun: getMapEmbeddableDisplayName(),
objectNoun: MAP_EMBEDDABLE_NAME,
currentObjectId: this.props.savedMap.getSavedObjectId()!,
otherObjectId: sharingSavedObjectProps.aliasTargetId!,
otherObjectPath: `${getEditPath(sharingSavedObjectProps.aliasTargetId!)}${

View file

@ -8,7 +8,7 @@
import { i18n } from '@kbn/i18n';
import { ScopedHistory } from '@kbn/core/public';
import { getCoreOverlays, getNavigateToApp } from '../../../kibana_services';
import { getAppTitle } from '../../../../common/i18n_getters';
import { APP_NAME } from '../../../../common/constants';
export const unsavedChangesWarning = i18n.translate(
'xpack.maps.breadCrumbs.unsavedChangesWarning',
@ -49,7 +49,7 @@ export function getBreadcrumbs({
if (!isByValue) {
breadcrumbs.push({
text: getAppTitle(),
text: APP_NAME,
onClick: async () => {
if (getHasUnsavedChanges()) {
const confirmed = await getCoreOverlays().openConfirm(unsavedChangesWarning, {

View file

@ -27,8 +27,8 @@ import {
getSavedObjectsTagging,
getPresentationUtilContext,
} from '../../kibana_services';
import { MAP_EMBEDDABLE_NAME } from '../../../common/constants';
import { SavedMap } from './saved_map';
import { getMapEmbeddableDisplayName } from '../../../common/i18n_getters';
import { checkForDuplicateTitle } from '../../content_management';
const SavedObjectSaveModalDashboard = withSuspense(LazySavedObjectSaveModalDashboard);
@ -179,7 +179,7 @@ export function getTopNavConfig({
copyOnSave: props.newCopyOnSave,
lastSavedTitle: savedMap.getSavedObjectId() ? savedMap.getTitle() : '',
isTitleDuplicateConfirmed: props.isTitleDuplicateConfirmed,
getDisplayName: getMapEmbeddableDisplayName,
getDisplayName: () => MAP_EMBEDDABLE_NAME,
onTitleDuplicate: props.onTitleDuplicate,
},
{

View file

@ -5,25 +5,13 @@
* 2.0.
*/
import React from 'react';
import { i18n } from '@kbn/i18n';
import { Embeddable, EmbeddableInput } from '@kbn/embeddable-plugin/public';
import { createReactOverlays } from '@kbn/kibana-react-plugin/public';
import type { Embeddable } from '@kbn/embeddable-plugin/public';
import { createAction } from '@kbn/ui-actions-plugin/public';
import { isLegacyMap } from '../legacy_visualizations';
import { MAP_SAVED_OBJECT_TYPE } from '../../common/constants';
import { getCore } from '../kibana_services';
import type { FilterByMapExtentActionContext, FilterByMapExtentInput } from './types';
export const FILTER_BY_MAP_EXTENT = 'FILTER_BY_MAP_EXTENT';
interface FilterByMapExtentInput extends EmbeddableInput {
filterByMapExtent: boolean;
}
interface FilterByMapExtentActionContext {
embeddable: Embeddable<FilterByMapExtentInput>;
}
function getContainerLabel(embeddable: Embeddable<FilterByMapExtentInput>) {
return embeddable.parent?.type === 'dashboard'
? i18n.translate('xpack.maps.filterByMapExtentMenuItem.dashboardLabel', {
@ -58,20 +46,12 @@ export const filterByMapExtentAction = createAction<FilterByMapExtentActionConte
getIconType: () => {
return 'filter';
},
isCompatible: async ({ embeddable }: FilterByMapExtentActionContext) => {
return (
(embeddable.type === MAP_SAVED_OBJECT_TYPE || isLegacyMap(embeddable)) &&
!embeddable.getInput().disableTriggers
);
isCompatible: async (context: FilterByMapExtentActionContext) => {
const { isCompatible } = await import('./is_compatible');
return isCompatible(context);
},
execute: async (context: FilterByMapExtentActionContext) => {
const { FilterByMapExtentModal } = await import('./filter_by_map_extent_modal');
const { openModal } = createReactOverlays(getCore());
const modalSession = openModal(
<FilterByMapExtentModal
onClose={() => modalSession.close()}
title={getDisplayName(context.embeddable)}
/>
);
const { openModal } = await import('./modal');
openModal(getDisplayName(context.embeddable));
},
});

View file

@ -0,0 +1,17 @@
/*
* 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 { MAP_SAVED_OBJECT_TYPE } from '../../../common/constants';
import { isLegacyMap } from '../../legacy_visualizations/is_legacy_map';
import type { FilterByMapExtentActionContext } from './types';
export function isCompatible({ embeddable }: FilterByMapExtentActionContext) {
return (
(embeddable.type === MAP_SAVED_OBJECT_TYPE || isLegacyMap(embeddable)) &&
!embeddable.getInput().disableTriggers
);
}

View file

@ -14,14 +14,23 @@ import {
EuiSwitch,
EuiSwitchEvent,
} from '@elastic/eui';
import { mapEmbeddablesSingleton } from '../embeddable/map_embeddables_singleton';
import { createReactOverlays } from '@kbn/kibana-react-plugin/public';
import { mapEmbeddablesSingleton } from '../../embeddable/map_embeddables_singleton';
import { getCore } from '../../kibana_services';
export function openModal(title: string) {
const { openModal: reactOverlaysOpenModal } = createReactOverlays(getCore());
const modalSession = reactOverlaysOpenModal(
<FilterByMapExtentModal onClose={() => modalSession.close()} title={title} />
);
}
interface Props {
onClose: () => void;
title: string;
}
export class FilterByMapExtentModal extends Component<Props> {
class FilterByMapExtentModal extends Component<Props> {
_renderSwitches() {
return mapEmbeddablesSingleton.getMapPanels().map((mapPanel) => {
return (

View file

@ -0,0 +1,16 @@
/*
* 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 type { Embeddable, EmbeddableInput } from '@kbn/embeddable-plugin/public';
export interface FilterByMapExtentInput extends EmbeddableInput {
filterByMapExtent: boolean;
}
export interface FilterByMapExtentActionContext {
embeddable: Embeddable<FilterByMapExtentInput>;
}

View file

@ -0,0 +1,50 @@
/*
* 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 { v4 as uuidv4 } from 'uuid';
import type { Query } from '@kbn/es-query';
import type { SerializableRecord } from '@kbn/utility-types';
import type { VisualizeFieldContext } from '@kbn/ui-actions-plugin/public';
import { getIndexPatternService, getData, getShareService } from '../kibana_services';
import { LAYER_TYPE, SOURCE_TYPES, SCALING_TYPES } from '../../common/constants';
import type { LayerDescriptor } from '../../common/descriptor_types';
import type { MapsAppLocator } from '../locators/map_locator/types';
import { MAPS_APP_LOCATOR } from '../locators/map_locator/locator_definition';
export const getMapsLink = async (context: VisualizeFieldContext) => {
const dataView = await getIndexPatternService().get(context.dataViewSpec.id!);
// create initial layer descriptor
const hasTooltips =
context?.contextualFields?.length && context?.contextualFields[0] !== '_source';
const initialLayers = [
{
id: uuidv4(),
visible: true,
type: LAYER_TYPE.MVT_VECTOR,
sourceDescriptor: {
id: uuidv4(),
type: SOURCE_TYPES.ES_SEARCH,
tooltipProperties: hasTooltips ? context.contextualFields : [],
label: dataView.getIndexPattern(),
indexPatternId: context.dataViewSpec.id,
geoField: context.fieldName,
scalingType: SCALING_TYPES.MVT,
},
},
];
const locator = getShareService().url.locators.get(MAPS_APP_LOCATOR) as MapsAppLocator;
const location = await locator.getLocation({
filters: getData().query.filterManager.getFilters(),
query: getData().query.queryString.getQuery() as Query,
initialLayers: initialLayers as unknown as LayerDescriptor[] & SerializableRecord,
timeRange: getData().query.timefilter.timefilter.getTime(),
dataViewSpec: context.dataViewSpec,
});
return location;
};

View file

@ -0,0 +1,40 @@
/*
* 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 { createAction } from '@kbn/ui-actions-plugin/public';
import type { SynchronizeMovementActionContext } from './types';
export const SYNCHRONIZE_MOVEMENT_ACTION = 'SYNCHRONIZE_MOVEMENT_ACTION';
export const synchronizeMovementAction = createAction<SynchronizeMovementActionContext>({
id: SYNCHRONIZE_MOVEMENT_ACTION,
type: SYNCHRONIZE_MOVEMENT_ACTION,
order: 21,
getDisplayName: ({ embeddable }: SynchronizeMovementActionContext) => {
return i18n.translate('xpack.maps.synchronizeMovementAction.title', {
defaultMessage: 'Synchronize map movement',
});
},
getDisplayNameTooltip: () => {
return i18n.translate('xpack.maps.synchronizeMovementAction.tooltipContent', {
defaultMessage:
'Synchronize maps, so that if you zoom and pan in one map, the movement is reflected in other maps',
});
},
getIconType: () => {
return 'crosshairs';
},
isCompatible: async (context: SynchronizeMovementActionContext) => {
const { isCompatible } = await import('./is_compatible');
return isCompatible(context);
},
execute: async (context: SynchronizeMovementActionContext) => {
const { openModal } = await import('./modal');
openModal();
},
});

View file

@ -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 type { Embeddable as LensEmbeddable } from '@kbn/lens-plugin/public';
import { MAP_SAVED_OBJECT_TYPE } from '../../../common/constants';
import { isLegacyMap } from '../../legacy_visualizations/is_legacy_map';
import { mapEmbeddablesSingleton } from '../../embeddable/map_embeddables_singleton';
import type { SynchronizeMovementActionContext } from './types';
export function isCompatible({ embeddable }: SynchronizeMovementActionContext) {
if (!mapEmbeddablesSingleton.hasMultipleMaps()) {
return false;
}
if (
embeddable.type === 'lens' &&
typeof (embeddable as LensEmbeddable).getSavedVis === 'function' &&
(embeddable as LensEmbeddable).getSavedVis()?.visualizationType === 'lnsChoropleth'
) {
return true;
}
if (isLegacyMap(embeddable)) {
return true;
}
return embeddable.type === MAP_SAVED_OBJECT_TYPE;
}

View file

@ -15,13 +15,22 @@ import {
EuiSwitch,
EuiSwitchEvent,
} from '@elastic/eui';
import { mapEmbeddablesSingleton } from '../embeddable/map_embeddables_singleton';
import { createReactOverlays } from '@kbn/kibana-react-plugin/public';
import { mapEmbeddablesSingleton } from '../../embeddable/map_embeddables_singleton';
import { getCore } from '../../kibana_services';
export function openModal() {
const { openModal: reactOverlaysOpenModal } = createReactOverlays(getCore());
const modalSession = reactOverlaysOpenModal(
<SynchronizeMovementModal onClose={() => modalSession.close()} />
);
}
interface Props {
onClose: () => void;
}
export class SynchronizeMovementModal extends Component<Props> {
class SynchronizeMovementModal extends Component<Props> {
_renderSwitches() {
const mapPanels = mapEmbeddablesSingleton.getMapPanels();

View file

@ -0,0 +1,12 @@
/*
* 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 type { Embeddable, EmbeddableInput } from '@kbn/embeddable-plugin/public';
export interface SynchronizeMovementActionContext {
embeddable: Embeddable<EmbeddableInput>;
}

View file

@ -1,69 +0,0 @@
/*
* 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 { i18n } from '@kbn/i18n';
import { createReactOverlays } from '@kbn/kibana-react-plugin/public';
import { Embeddable, EmbeddableInput } from '@kbn/embeddable-plugin/public';
import { createAction } from '@kbn/ui-actions-plugin/public';
import type { Embeddable as LensEmbeddable } from '@kbn/lens-plugin/public';
import { isLegacyMap } from '../legacy_visualizations';
import { MAP_SAVED_OBJECT_TYPE } from '../../common/constants';
import { getCore } from '../kibana_services';
export const SYNCHRONIZE_MOVEMENT_ACTION = 'SYNCHRONIZE_MOVEMENT_ACTION';
interface SynchronizeMovementActionContext {
embeddable: Embeddable<EmbeddableInput>;
}
export const synchronizeMovementAction = createAction<SynchronizeMovementActionContext>({
id: SYNCHRONIZE_MOVEMENT_ACTION,
type: SYNCHRONIZE_MOVEMENT_ACTION,
order: 21,
getDisplayName: ({ embeddable }: SynchronizeMovementActionContext) => {
return i18n.translate('xpack.maps.synchronizeMovementAction.title', {
defaultMessage: 'Synchronize map movement',
});
},
getDisplayNameTooltip: () => {
return i18n.translate('xpack.maps.synchronizeMovementAction.tooltipContent', {
defaultMessage:
'Synchronize maps, so that if you zoom and pan in one map, the movement is reflected in other maps',
});
},
getIconType: () => {
return 'crosshairs';
},
isCompatible: async ({ embeddable }: SynchronizeMovementActionContext) => {
const { mapEmbeddablesSingleton } = await import('../embeddable/map_embeddables_singleton');
if (!mapEmbeddablesSingleton.hasMultipleMaps()) {
return false;
}
if (
embeddable.type === 'lens' &&
typeof (embeddable as LensEmbeddable).getSavedVis === 'function' &&
(embeddable as LensEmbeddable).getSavedVis()?.visualizationType === 'lnsChoropleth'
) {
return true;
}
if (isLegacyMap(embeddable)) {
return true;
}
return embeddable.type === MAP_SAVED_OBJECT_TYPE;
},
execute: async ({ embeddable }: SynchronizeMovementActionContext) => {
const { SynchronizeMovementModal } = await import('./synchronize_movement_modal');
const { openModal } = createReactOverlays(getCore());
const modalSession = openModal(
<SynchronizeMovementModal onClose={() => modalSession.close()} />
);
},
});

View file

@ -5,10 +5,7 @@
* 2.0.
*/
import { v4 as uuidv4 } from 'uuid';
import { i18n } from '@kbn/i18n';
import type { Query } from '@kbn/es-query';
import type { SerializableRecord } from '@kbn/utility-types';
import { METRIC_TYPE } from '@kbn/analytics';
import {
createAction,
@ -18,16 +15,7 @@ import {
import { getUsageCollection } from '../kibana_services';
import { APP_ID } from '../../common/constants';
import {
getVisualizeCapabilities,
getIndexPatternService,
getData,
getShareService,
getCore,
} from '../kibana_services';
import { MapsAppLocator, MAPS_APP_LOCATOR } from '../locators';
import { LAYER_TYPE, SOURCE_TYPES, SCALING_TYPES } from '../../common/constants';
import { LayerDescriptor } from '../../common/descriptor_types';
import { getVisualizeCapabilities, getCore } from '../kibana_services';
export const visualizeGeoFieldAction = createAction<VisualizeFieldContext>({
id: ACTION_VISUALIZE_GEO_FIELD,
@ -38,6 +26,7 @@ export const visualizeGeoFieldAction = createAction<VisualizeFieldContext>({
}),
isCompatible: async () => !!getVisualizeCapabilities().show,
getHref: async (context) => {
const { getMapsLink } = await import('./get_maps_link');
const { app, path } = await getMapsLink(context);
return getCore().application.getUrlForApp(app, {
@ -46,6 +35,7 @@ export const visualizeGeoFieldAction = createAction<VisualizeFieldContext>({
});
},
execute: async (context) => {
const { getMapsLink } = await import('./get_maps_link');
const { app, path, state } = await getMapsLink(context);
const usageCollection = getUsageCollection();
@ -61,37 +51,3 @@ export const visualizeGeoFieldAction = createAction<VisualizeFieldContext>({
});
},
});
const getMapsLink = async (context: VisualizeFieldContext) => {
const dataView = await getIndexPatternService().get(context.dataViewSpec.id!);
// create initial layer descriptor
const hasTooltips =
context?.contextualFields?.length && context?.contextualFields[0] !== '_source';
const initialLayers = [
{
id: uuidv4(),
visible: true,
type: LAYER_TYPE.MVT_VECTOR,
sourceDescriptor: {
id: uuidv4(),
type: SOURCE_TYPES.ES_SEARCH,
tooltipProperties: hasTooltips ? context.contextualFields : [],
label: dataView.getIndexPattern(),
indexPatternId: context.dataViewSpec.id,
geoField: context.fieldName,
scalingType: SCALING_TYPES.MVT,
},
},
];
const locator = getShareService().url.locators.get(MAPS_APP_LOCATOR) as MapsAppLocator;
const location = await locator.getLocation({
filters: getData().query.filterManager.getFilters(),
query: getData().query.queryString.getQuery() as Query,
initialLayers: initialLayers as unknown as LayerDescriptor[] & SerializableRecord,
timeRange: getData().query.timefilter.timefilter.getTime(),
dataViewSpec: context.dataViewSpec,
});
return location;
};

View file

@ -6,11 +6,12 @@
*/
import { EMSClient, FileLayer, TMSService } from '@elastic/ems-client';
import { getTilemap, getEMSSettings, getMapsEmsStart } from './kibana_services';
import { getEMSSettings, getMapsEmsStart } from './kibana_services';
import { getLicenseId } from './licensed_features';
export function getKibanaTileMap(): unknown {
return getTilemap();
const mapsEms = getMapsEmsStart();
return mapsEms.config.tilemap ? mapsEms.config.tilemap : {};
}
export async function getEmsFileLayers(): Promise<FileLayer[]> {

View file

@ -20422,7 +20422,6 @@
"xpack.maps.addLayerPanel.changeDataSourceButtonLabel": "Changer de calque",
"xpack.maps.addLayerPanel.footer.cancelButtonLabel": "Annuler",
"xpack.maps.aggs.defaultCountLabel": "compte",
"xpack.maps.appTitle": "Cartes",
"xpack.maps.attribution.addBtnAriaLabel": "Ajouter une attribution",
"xpack.maps.attribution.addBtnLabel": "Ajouter une attribution",
"xpack.maps.attribution.applyBtnLabel": "Appliquer",

View file

@ -20422,7 +20422,6 @@
"xpack.maps.addLayerPanel.changeDataSourceButtonLabel": "レイヤーを変更",
"xpack.maps.addLayerPanel.footer.cancelButtonLabel": "キャンセル",
"xpack.maps.aggs.defaultCountLabel": "カウント",
"xpack.maps.appTitle": "マップ",
"xpack.maps.attribution.addBtnAriaLabel": "属性を追加",
"xpack.maps.attribution.addBtnLabel": "属性を追加",
"xpack.maps.attribution.applyBtnLabel": "適用",

View file

@ -20422,7 +20422,6 @@
"xpack.maps.addLayerPanel.changeDataSourceButtonLabel": "更改图层",
"xpack.maps.addLayerPanel.footer.cancelButtonLabel": "取消",
"xpack.maps.aggs.defaultCountLabel": "计数",
"xpack.maps.appTitle": "Maps",
"xpack.maps.attribution.addBtnAriaLabel": "添加归因",
"xpack.maps.attribution.addBtnLabel": "添加归因",
"xpack.maps.attribution.applyBtnLabel": "应用",