[Maps] Separate layer wizards for Clusters and heatmap (#60870)

* [Maps] source registry and register seperate clusters and heat map sources

* split into to registries

* add EMS file source

* add geojson upload layer

* register rest of sources

* i18n changes

* ts lint errors

* fix jest test

* fix pew-pew source

* review feedback

* import registires in plugin so they exist in embeddable

* remove order parameter and move all layer registies into single file

* fix functionalt est

* pass constructor to sourceREgistry instead of factory

* review feedback

Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
This commit is contained in:
Nathan Reese 2020-04-01 20:21:18 -06:00 committed by GitHub
parent 325f8e0ad5
commit 467f27b600
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
36 changed files with 635 additions and 422 deletions

View file

@ -5,13 +5,12 @@
*/
import React, { Fragment } from 'react';
import { GeojsonFileSource } from '../../../layers/sources/client_file_source';
import { EuiSpacer, EuiPanel, EuiButtonEmpty } from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react';
import { uploadLayerWizardConfig } from '../../../layers/sources/client_file_source';
export const ImportEditor = ({ clearSource, isIndexingTriggered, ...props }) => {
const editorProperties = getEditorProperties({ isIndexingTriggered, ...props });
const editor = GeojsonFileSource.renderEditor(editorProperties);
return (
<Fragment>
{isIndexingTriggered ? null : (
@ -25,7 +24,9 @@ export const ImportEditor = ({ clearSource, isIndexingTriggered, ...props }) =>
<EuiSpacer size="s" />
</Fragment>
)}
<EuiPanel style={{ position: 'relative' }}>{editor}</EuiPanel>
<EuiPanel style={{ position: 'relative' }}>
{uploadLayerWizardConfig.renderWizard(editorProperties)}
</EuiPanel>
</Fragment>
);
};

View file

@ -5,28 +5,20 @@
*/
import React, { Fragment } from 'react';
import { ALL_SOURCES } from '../../../layers/sources/all_sources';
import { EuiSpacer, EuiPanel, EuiButtonEmpty } from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react';
export const SourceEditor = ({
clearSource,
sourceType,
layerWizard,
isIndexingTriggered,
inspectorAdapters,
previewLayer,
}) => {
const editorProperties = {
onPreviewSource: previewLayer,
inspectorAdapters,
};
const Source = ALL_SOURCES.find(Source => {
return Source.type === sourceType;
});
if (!Source) {
throw new Error(`Unexpected source type: ${sourceType}`);
if (!layerWizard) {
return null;
}
const editor = Source.renderEditor(editorProperties);
return (
<Fragment>
{isIndexingTriggered ? null : (
@ -40,7 +32,9 @@ export const SourceEditor = ({
<EuiSpacer size="s" />
</Fragment>
)}
<EuiPanel>{editor}</EuiPanel>
<EuiPanel>
{layerWizard.renderWizard({ onPreviewSource: previewLayer, inspectorAdapters })}
</EuiPanel>
</Fragment>
);
};

View file

@ -5,30 +5,33 @@
*/
import React, { Fragment } from 'react';
import { ALL_SOURCES } from '../../../layers/sources/all_sources';
import { getLayerWizards } from '../../../layers/layer_wizard_registry';
import { EuiTitle, EuiSpacer, EuiCard, EuiIcon } from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react';
import _ from 'lodash';
export function SourceSelect({ updateSourceSelection }) {
const sourceCards = ALL_SOURCES.map(Source => {
const icon = Source.icon ? <EuiIcon type={Source.icon} size="l" /> : null;
const sourceCards = getLayerWizards().map(layerWizard => {
const icon = layerWizard.icon ? <EuiIcon type={layerWizard.icon} size="l" /> : null;
const sourceTitle = Source.title;
const onClick = () => {
updateSourceSelection({
layerWizard: layerWizard,
isIndexingSource: !!layerWizard.isIndexingSource,
});
};
return (
<Fragment key={Source.type}>
<Fragment key={layerWizard.title}>
<EuiSpacer size="s" />
<EuiCard
className="mapLayerAddpanel__card"
title={sourceTitle}
title={layerWizard.title}
icon={icon}
onClick={() =>
updateSourceSelection({ type: Source.type, isIndexingSource: Source.isIndexingSource })
}
description={Source.description}
onClick={onClick}
description={layerWizard.description}
layout="horizontal"
data-test-subj={_.camelCase(Source.title)}
data-test-subj={_.camelCase(layerWizard.title)}
/>
</Fragment>
);

View file

@ -14,7 +14,7 @@ import { i18n } from '@kbn/i18n';
export class AddLayerPanel extends Component {
state = {
sourceType: null,
layerWizard: null,
layer: null,
importView: false,
layerImportAddReady: false,
@ -35,9 +35,9 @@ export class AddLayerPanel extends Component {
}
_getPanelDescription() {
const { sourceType, importView, layerImportAddReady } = this.state;
const { layerWizard, importView, layerImportAddReady } = this.state;
let panelDescription;
if (!sourceType) {
if (!layerWizard) {
panelDescription = i18n.translate('xpack.maps.addLayerPanel.selectSource', {
defaultMessage: 'Select source',
});
@ -85,13 +85,13 @@ export class AddLayerPanel extends Component {
this.setState({
layer: null,
...(!keepSourceType ? { sourceType: null, importView: false } : {}),
...(!keepSourceType ? { layerWizard: null, importView: false } : {}),
});
this.props.removeTransientLayer();
};
_onSourceSelectionChange = ({ type, isIndexingSource }) => {
this.setState({ sourceType: type, importView: isIndexingSource });
_onSourceSelectionChange = ({ layerWizard, isIndexingSource }) => {
this.setState({ layerWizard, importView: isIndexingSource });
};
_layerAddHandler = () => {
@ -118,8 +118,8 @@ export class AddLayerPanel extends Component {
};
_renderAddLayerPanel() {
const { sourceType, importView } = this.state;
if (!sourceType) {
const { layerWizard, importView } = this.state;
if (!layerWizard) {
return <SourceSelect updateSourceSelection={this._onSourceSelectionChange} />;
}
if (importView) {
@ -134,7 +134,7 @@ export class AddLayerPanel extends Component {
return (
<SourceEditor
clearSource={this._clearLayerData}
sourceType={sourceType}
layerWizard={layerWizard}
previewLayer={this._viewLayer}
/>
);
@ -148,7 +148,7 @@ export class AddLayerPanel extends Component {
return (
<FlyoutFooter
showNextButton={!!this.state.sourceType}
showNextButton={!!this.state.layerWizard}
disableNextButton={!buttonEnabled}
onClick={this._layerAddHandler}
nextButtonText={buttonDescription}

View file

@ -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;
* you may not use this file except in compliance with the Elastic License.
*/
/* eslint-disable @typescript-eslint/consistent-type-definitions */
type LayerWizard = {
description: string;
icon: string;
isIndexingSource?: boolean;
renderWizard({
onPreviewSource,
inspectorAdapters,
}: {
onPreviewSource: () => void;
inspectorAdapters: unknown;
}): unknown;
title: string;
};
const registry: LayerWizard[] = [];
export function registerLayerWizard(layerWizard: LayerWizard) {
registry.push(layerWizard);
}
export function getLayerWizards(): LayerWizard[] {
return [...registry];
}

View file

@ -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;
* you may not use this file except in compliance with the Elastic License.
*/
import { registerLayerWizard } from './layer_wizard_registry';
import { uploadLayerWizardConfig } from './sources/client_file_source';
import { esDocumentsLayerWizardConfig } from './sources/es_search_source';
import { clustersLayerWizardConfig, heatmapLayerWizardConfig } from './sources/es_geo_grid_source';
import { point2PointLayerWizardConfig } from './sources/es_pew_pew_source/es_pew_pew_source';
import { emsBoundariesLayerWizardConfig } from './sources/ems_file_source';
import { emsBaseMapLayerWizardConfig } from './sources/ems_tms_source';
import { kibanaRegionMapLayerWizardConfig } from './sources/kibana_regionmap_source';
import { kibanaBasemapLayerWizardConfig } from './sources/kibana_tilemap_source';
import { tmsLayerWizardConfig } from './sources/xyz_tms_source';
import { wmsLayerWizardConfig } from './sources/wms_source';
// Registration order determines display order
registerLayerWizard(uploadLayerWizardConfig);
registerLayerWizard(esDocumentsLayerWizardConfig);
registerLayerWizard(clustersLayerWizardConfig);
registerLayerWizard(heatmapLayerWizardConfig);
registerLayerWizard(point2PointLayerWizardConfig);
registerLayerWizard(emsBoundariesLayerWizardConfig);
registerLayerWizard(emsBaseMapLayerWizardConfig);
registerLayerWizard(kibanaRegionMapLayerWizardConfig);
registerLayerWizard(kibanaBasemapLayerWizardConfig);
registerLayerWizard(tmsLayerWizardConfig);
registerLayerWizard(wmsLayerWizardConfig);

View file

@ -1,29 +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;
* you may not use this file except in compliance with the Elastic License.
*/
import { EMSFileSource } from './ems_file_source';
import { GeojsonFileSource } from './client_file_source';
import { KibanaRegionmapSource } from './kibana_regionmap_source';
import { XYZTMSSource } from './xyz_tms_source';
import { EMSTMSSource } from './ems_tms_source';
import { WMSSource } from './wms_source';
import { KibanaTilemapSource } from './kibana_tilemap_source';
import { ESGeoGridSource } from './es_geo_grid_source';
import { ESSearchSource } from './es_search_source';
import { ESPewPewSource } from './es_pew_pew_source/es_pew_pew_source';
export const ALL_SOURCES = [
GeojsonFileSource,
ESSearchSource,
ESGeoGridSource,
ESPewPewSource,
EMSFileSource,
EMSTMSSource,
KibanaRegionmapSource,
KibanaTilemapSource,
XYZTMSSource,
WMSSource,
];

View file

@ -16,16 +16,11 @@ import { ESSearchSource } from '../es_search_source';
import uuid from 'uuid/v4';
import _ from 'lodash';
import { i18n } from '@kbn/i18n';
import { registerSource } from '../source_registry';
export class GeojsonFileSource extends AbstractVectorSource {
static type = GEOJSON_FILE;
static title = i18n.translate('xpack.maps.source.geojsonFileTitle', {
defaultMessage: 'Uploaded GeoJSON',
});
static description = i18n.translate('xpack.maps.source.geojsonFileDescription', {
defaultMessage: 'Upload and index GeoJSON data in Elasticsearch',
});
static icon = 'importAction';
static isIndexingSource = true;
static createDescriptor(geoJson, name) {
@ -59,90 +54,6 @@ export class GeojsonFileSource extends AbstractVectorSource {
};
}
static viewIndexedData = (
addAndViewSource,
inspectorAdapters,
importSuccessHandler,
importErrorHandler
) => {
return (indexResponses = {}) => {
const { indexDataResp, indexPatternResp } = indexResponses;
const indexCreationFailed = !(indexDataResp && indexDataResp.success);
const allDocsFailed = indexDataResp.failures.length === indexDataResp.docCount;
const indexPatternCreationFailed = !(indexPatternResp && indexPatternResp.success);
if (indexCreationFailed || allDocsFailed || indexPatternCreationFailed) {
importErrorHandler(indexResponses);
return;
}
const { fields, id } = indexPatternResp;
const geoFieldArr = fields.filter(field =>
Object.values(ES_GEO_FIELD_TYPE).includes(field.type)
);
const geoField = _.get(geoFieldArr, '[0].name');
const indexPatternId = id;
if (!indexPatternId || !geoField) {
addAndViewSource(null);
} else {
// Only turn on bounds filter for large doc counts
const filterByMapBounds = indexDataResp.docCount > DEFAULT_MAX_RESULT_WINDOW;
const source = new ESSearchSource(
{
id: uuid(),
indexPatternId,
geoField,
filterByMapBounds,
},
inspectorAdapters
);
addAndViewSource(source);
importSuccessHandler(indexResponses);
}
};
};
static previewGeojsonFile = (onPreviewSource, inspectorAdapters) => {
return (geojsonFile, name) => {
if (!geojsonFile) {
onPreviewSource(null);
return;
}
const sourceDescriptor = GeojsonFileSource.createDescriptor(geojsonFile, name);
const source = new GeojsonFileSource(sourceDescriptor, inspectorAdapters);
onPreviewSource(source);
};
};
static renderEditor({
onPreviewSource,
inspectorAdapters,
addAndViewSource,
isIndexingTriggered,
onRemove,
onIndexReady,
importSuccessHandler,
importErrorHandler,
}) {
return (
<ClientFileCreateSourceEditor
previewGeojsonFile={GeojsonFileSource.previewGeojsonFile(
onPreviewSource,
inspectorAdapters
)}
isIndexingTriggered={isIndexingTriggered}
onIndexingComplete={GeojsonFileSource.viewIndexedData(
addAndViewSource,
inspectorAdapters,
importSuccessHandler,
importErrorHandler
)}
onRemove={onRemove}
onIndexReady={onIndexReady}
/>
);
}
async getGeoJsonWithMeta() {
return {
data: this._descriptor.__featureCollection,
@ -162,3 +73,99 @@ export class GeojsonFileSource extends AbstractVectorSource {
return GeojsonFileSource.isIndexingSource;
}
}
const viewIndexedData = (
addAndViewSource,
inspectorAdapters,
importSuccessHandler,
importErrorHandler
) => {
return (indexResponses = {}) => {
const { indexDataResp, indexPatternResp } = indexResponses;
const indexCreationFailed = !(indexDataResp && indexDataResp.success);
const allDocsFailed = indexDataResp.failures.length === indexDataResp.docCount;
const indexPatternCreationFailed = !(indexPatternResp && indexPatternResp.success);
if (indexCreationFailed || allDocsFailed || indexPatternCreationFailed) {
importErrorHandler(indexResponses);
return;
}
const { fields, id } = indexPatternResp;
const geoFieldArr = fields.filter(field =>
Object.values(ES_GEO_FIELD_TYPE).includes(field.type)
);
const geoField = _.get(geoFieldArr, '[0].name');
const indexPatternId = id;
if (!indexPatternId || !geoField) {
addAndViewSource(null);
} else {
// Only turn on bounds filter for large doc counts
const filterByMapBounds = indexDataResp.docCount > DEFAULT_MAX_RESULT_WINDOW;
const source = new ESSearchSource(
{
id: uuid(),
indexPatternId,
geoField,
filterByMapBounds,
},
inspectorAdapters
);
addAndViewSource(source);
importSuccessHandler(indexResponses);
}
};
};
const previewGeojsonFile = (onPreviewSource, inspectorAdapters) => {
return (geojsonFile, name) => {
if (!geojsonFile) {
onPreviewSource(null);
return;
}
const sourceDescriptor = GeojsonFileSource.createDescriptor(geojsonFile, name);
const source = new GeojsonFileSource(sourceDescriptor, inspectorAdapters);
onPreviewSource(source);
};
};
registerSource({
ConstructorFunction: GeojsonFileSource,
type: GEOJSON_FILE,
});
export const uploadLayerWizardConfig = {
description: i18n.translate('xpack.maps.source.geojsonFileDescription', {
defaultMessage: 'Index GeoJSON data in Elasticsearch',
}),
icon: 'importAction',
isIndexingSource: true,
renderWizard: ({
onPreviewSource,
inspectorAdapters,
addAndViewSource,
isIndexingTriggered,
onRemove,
onIndexReady,
importSuccessHandler,
importErrorHandler,
}) => {
return (
<ClientFileCreateSourceEditor
previewGeojsonFile={previewGeojsonFile(onPreviewSource, inspectorAdapters)}
isIndexingTriggered={isIndexingTriggered}
onIndexingComplete={viewIndexedData(
addAndViewSource,
inspectorAdapters,
importSuccessHandler,
importErrorHandler
)}
onRemove={onRemove}
onIndexReady={onIndexReady}
/>
);
},
title: i18n.translate('xpack.maps.source.geojsonFileTitle', {
defaultMessage: 'Upload GeoJSON',
}),
};

View file

@ -4,4 +4,4 @@
* you may not use this file except in compliance with the Elastic License.
*/
export { GeojsonFileSource } from './geojson_file_source';
export { GeojsonFileSource, uploadLayerWizardConfig } from './geojson_file_source';

View file

@ -14,16 +14,14 @@ import { i18n } from '@kbn/i18n';
import { getDataSourceLabel } from '../../../../common/i18n_getters';
import { UpdateSourceEditor } from './update_source_editor';
import { EMSFileField } from '../../fields/ems_file_field';
import { registerSource } from '../source_registry';
const sourceTitle = i18n.translate('xpack.maps.source.emsFileTitle', {
defaultMessage: 'EMS Boundaries',
});
export class EMSFileSource extends AbstractVectorSource {
static type = EMS_FILE;
static title = i18n.translate('xpack.maps.source.emsFileTitle', {
defaultMessage: 'EMS Boundaries',
});
static description = i18n.translate('xpack.maps.source.emsFileDescription', {
defaultMessage: 'Administrative boundaries from Elastic Maps Service',
});
static icon = 'emsApp';
static createDescriptor({ id, tooltipProperties = [] }) {
return {
@ -33,15 +31,6 @@ export class EMSFileSource extends AbstractVectorSource {
};
}
static renderEditor({ onPreviewSource, inspectorAdapters }) {
const onSourceConfigChange = sourceConfig => {
const sourceDescriptor = EMSFileSource.createDescriptor(sourceConfig);
const source = new EMSFileSource(sourceDescriptor, inspectorAdapters);
onPreviewSource(source);
};
return <EMSFileCreateSourceEditor onSourceConfigChange={onSourceConfigChange} />;
}
constructor(descriptor, inspectorAdapters) {
super(EMSFileSource.createDescriptor(descriptor), inspectorAdapters);
this._tooltipFields = this._descriptor.tooltipProperties.map(propertyKey =>
@ -118,7 +107,7 @@ export class EMSFileSource extends AbstractVectorSource {
return [
{
label: getDataSourceLabel(),
value: EMSFileSource.title,
value: sourceTitle,
},
{
label: i18n.translate('xpack.maps.source.emsFile.layerLabel', {
@ -167,3 +156,24 @@ export class EMSFileSource extends AbstractVectorSource {
return [VECTOR_SHAPE_TYPES.POLYGON];
}
}
registerSource({
ConstructorFunction: EMSFileSource,
type: EMS_FILE,
});
export const emsBoundariesLayerWizardConfig = {
description: i18n.translate('xpack.maps.source.emsFileDescription', {
defaultMessage: 'Administrative boundaries from Elastic Maps Service',
}),
icon: 'emsApp',
renderWizard: ({ onPreviewSource, inspectorAdapters }) => {
const onSourceConfigChange = sourceConfig => {
const sourceDescriptor = EMSFileSource.createDescriptor(sourceConfig);
const source = new EMSFileSource(sourceDescriptor, inspectorAdapters);
onPreviewSource(source);
};
return <EMSFileCreateSourceEditor onSourceConfigChange={onSourceConfigChange} />;
},
title: sourceTitle,
};

View file

@ -4,4 +4,4 @@
* you may not use this file except in compliance with the Elastic License.
*/
export { EMSFileSource } from './ems_file_source';
export { EMSFileSource, emsBoundariesLayerWizardConfig } from './ems_file_source';

View file

@ -16,16 +16,14 @@ import { i18n } from '@kbn/i18n';
import { getDataSourceLabel } from '../../../../common/i18n_getters';
import { EMS_TMS } from '../../../../common/constants';
import { getInjectedVarFunc, getUiSettings } from '../../../kibana_services';
import { registerSource } from '../source_registry';
const sourceTitle = i18n.translate('xpack.maps.source.emsTileTitle', {
defaultMessage: 'EMS Basemaps',
});
export class EMSTMSSource extends AbstractTMSSource {
static type = EMS_TMS;
static title = i18n.translate('xpack.maps.source.emsTileTitle', {
defaultMessage: 'EMS Basemaps',
});
static description = i18n.translate('xpack.maps.source.emsTileDescription', {
defaultMessage: 'Tile map service from Elastic Maps Service',
});
static icon = 'emsApp';
static createDescriptor(sourceConfig) {
return {
@ -35,16 +33,6 @@ export class EMSTMSSource extends AbstractTMSSource {
};
}
static renderEditor({ onPreviewSource, inspectorAdapters }) {
const onSourceConfigChange = sourceConfig => {
const descriptor = EMSTMSSource.createDescriptor(sourceConfig);
const source = new EMSTMSSource(descriptor, inspectorAdapters);
onPreviewSource(source);
};
return <TileServiceSelect onTileSelect={onSourceConfigChange} />;
}
constructor(descriptor, inspectorAdapters) {
super(
{
@ -69,7 +57,7 @@ export class EMSTMSSource extends AbstractTMSSource {
return [
{
label: getDataSourceLabel(),
value: EMSTMSSource.title,
value: sourceTitle,
},
{
label: i18n.translate('xpack.maps.source.emsTile.serviceId', {
@ -157,3 +145,25 @@ export class EMSTMSSource extends AbstractTMSSource {
return isDarkMode ? emsTileLayerId.dark : emsTileLayerId.bright;
}
}
registerSource({
ConstructorFunction: EMSTMSSource,
type: EMS_TMS,
});
export const emsBaseMapLayerWizardConfig = {
description: i18n.translate('xpack.maps.source.emsTileDescription', {
defaultMessage: 'Tile map service from Elastic Maps Service',
}),
icon: 'emsApp',
renderWizard: ({ onPreviewSource, inspectorAdapters }) => {
const onSourceConfigChange = sourceConfig => {
const descriptor = EMSTMSSource.createDescriptor(sourceConfig);
const source = new EMSTMSSource(descriptor, inspectorAdapters);
onPreviewSource(source);
};
return <TileServiceSelect onTileSelect={onSourceConfigChange} />;
},
title: sourceTitle,
};

View file

@ -4,4 +4,4 @@
* you may not use this file except in compliance with the Elastic License.
*/
export { EMSTMSSource } from './ems_tms_source';
export { EMSTMSSource, emsBaseMapLayerWizardConfig } from './ems_tms_source';

View file

@ -14,32 +14,12 @@ import { getIndexPatternService, getIndexPatternSelectComponent } from '../../..
import { NoIndexPatternCallout } from '../../../components/no_index_pattern_callout';
import { i18n } from '@kbn/i18n';
import { EuiFormRow, EuiComboBox, EuiSpacer } from '@elastic/eui';
import { EuiFormRow, EuiSpacer } from '@elastic/eui';
import {
AGGREGATABLE_GEO_FIELD_TYPES,
getAggregatableGeoFields,
} from '../../../index_pattern_util';
const requestTypeOptions = [
{
label: i18n.translate('xpack.maps.source.esGeoGrid.gridRectangleDropdownOption', {
defaultMessage: 'grid rectangles',
}),
value: RENDER_AS.GRID,
},
{
label: i18n.translate('xpack.maps.source.esGeoGrid.heatmapDropdownOption', {
defaultMessage: 'heat map',
}),
value: RENDER_AS.HEATMAP,
},
{
label: i18n.translate('xpack.maps.source.esGeoGrid.pointsDropdownOption', {
defaultMessage: 'clusters',
}),
value: RENDER_AS.POINT,
},
];
import { RenderAsSelect } from './render_as_select';
export class CreateSourceEditor extends Component {
static propTypes = {
@ -50,7 +30,7 @@ export class CreateSourceEditor extends Component {
isLoadingIndexPattern: false,
indexPatternId: '',
geoField: '',
requestType: requestTypeOptions[0],
requestType: this.props.requestType,
noGeoIndexPatternsExist: false,
};
@ -126,10 +106,10 @@ export class CreateSourceEditor extends Component {
);
};
_onRequestTypeSelect = selectedOptions => {
_onRequestTypeSelect = newValue => {
this.setState(
{
requestType: selectedOptions[0],
requestType: newValue,
},
this.previewLayer
);
@ -139,9 +119,7 @@ export class CreateSourceEditor extends Component {
const { indexPatternId, geoField, requestType } = this.state;
const sourceConfig =
indexPatternId && geoField
? { indexPatternId, geoField, requestType: requestType.value }
: null;
indexPatternId && geoField ? { indexPatternId, geoField, requestType } : null;
this.props.onSourceConfigChange(sourceConfig);
};
@ -176,28 +154,13 @@ export class CreateSourceEditor extends Component {
);
}
_renderLayerSelect() {
if (!this.state.indexPattern) {
_renderRenderAsSelect() {
if (this.state.requestType === RENDER_AS.HEATMAP || !this.state.indexPattern) {
return null;
}
return (
<EuiFormRow
label={i18n.translate('xpack.maps.source.esGeoGrid.showAsLabel', {
defaultMessage: 'Show as',
})}
>
<EuiComboBox
placeholder={i18n.translate('xpack.maps.source.esGeoGrid.showAsPlaceholder', {
defaultMessage: 'Select a single option',
})}
singleSelection={{ asPlainText: true }}
options={requestTypeOptions}
selectedOptions={[this.state.requestType]}
onChange={this._onRequestTypeSelect}
isClearable={false}
/>
</EuiFormRow>
<RenderAsSelect renderAs={this.state.requestType} onChange={this._onRequestTypeSelect} />
);
}
@ -243,7 +206,7 @@ export class CreateSourceEditor extends Component {
{this._renderNoIndexPatternWarning()}
{this._renderIndexPatternSelect()}
{this._renderGeoSelect()}
{this._renderLayerSelect()}
{this._renderRenderAsSelect()}
</Fragment>
);
}

View file

@ -32,17 +32,20 @@ import { AbstractESAggSource } from '../es_agg_source';
import { DynamicStyleProperty } from '../../styles/vector/properties/dynamic_style_property';
import { StaticStyleProperty } from '../../styles/vector/properties/static_style_property';
import { DataRequestAbortError } from '../../util/data_request';
import { registerSource } from '../source_registry';
export const MAX_GEOTILE_LEVEL = 29;
const clustersTitle = i18n.translate('xpack.maps.source.esGridClustersTitle', {
defaultMessage: 'Clusters and grids',
});
const heatmapTitle = i18n.translate('xpack.maps.source.esGridHeatmapTitle', {
defaultMessage: 'Heat map',
});
export class ESGeoGridSource extends AbstractESAggSource {
static type = ES_GEO_GRID;
static title = i18n.translate('xpack.maps.source.esGridTitle', {
defaultMessage: 'Grid aggregation',
});
static description = i18n.translate('xpack.maps.source.esGridDescription', {
defaultMessage: 'Geospatial data grouped in grids with metrics for each gridded cell',
});
static createDescriptor({ indexPatternId, geoField, requestType, resolution }) {
return {
@ -55,21 +58,6 @@ export class ESGeoGridSource extends AbstractESAggSource {
};
}
static renderEditor({ onPreviewSource, inspectorAdapters }) {
const onSourceConfigChange = sourceConfig => {
if (!sourceConfig) {
onPreviewSource(null);
return;
}
const sourceDescriptor = ESGeoGridSource.createDescriptor(sourceConfig);
const source = new ESGeoGridSource(sourceDescriptor, inspectorAdapters);
onPreviewSource(source);
};
return <CreateSourceEditor onSourceConfigChange={onSourceConfigChange} />;
}
renderSourceSettingsEditor({ onChange }) {
return (
<UpdateSourceEditor
@ -94,7 +82,7 @@ export class ESGeoGridSource extends AbstractESAggSource {
return [
{
label: getDataSourceLabel(),
value: ESGeoGridSource.title,
value: this._descriptor.requestType === RENDER_AS.HEATMAP ? heatmapTitle : clustersTitle,
},
{
label: i18n.translate('xpack.maps.source.esGrid.indexPatternLabel', {
@ -108,12 +96,6 @@ export class ESGeoGridSource extends AbstractESAggSource {
}),
value: this._descriptor.geoField,
},
{
label: i18n.translate('xpack.maps.source.esGrid.showasFieldLabel', {
defaultMessage: 'Show as',
}),
value: this._descriptor.requestType,
},
];
}
@ -429,3 +411,62 @@ export class ESGeoGridSource extends AbstractESAggSource {
return [VECTOR_SHAPE_TYPES.POINT];
}
}
registerSource({
ConstructorFunction: ESGeoGridSource,
type: ES_GEO_GRID,
});
export const clustersLayerWizardConfig = {
description: i18n.translate('xpack.maps.source.esGridClustersDescription', {
defaultMessage: 'Geospatial data grouped in grids with metrics for each gridded cell',
}),
icon: 'logoElasticsearch',
renderWizard: ({ onPreviewSource, inspectorAdapters }) => {
const onSourceConfigChange = sourceConfig => {
if (!sourceConfig) {
onPreviewSource(null);
return;
}
const sourceDescriptor = ESGeoGridSource.createDescriptor(sourceConfig);
const source = new ESGeoGridSource(sourceDescriptor, inspectorAdapters);
onPreviewSource(source);
};
return (
<CreateSourceEditor
requestType={RENDER_AS.POINT}
onSourceConfigChange={onSourceConfigChange}
/>
);
},
title: clustersTitle,
};
export const heatmapLayerWizardConfig = {
description: i18n.translate('xpack.maps.source.esGridHeatmapDescription', {
defaultMessage: 'Geospatial data grouped in grids to show density',
}),
icon: 'logoElasticsearch',
renderWizard: ({ onPreviewSource, inspectorAdapters }) => {
const onSourceConfigChange = sourceConfig => {
if (!sourceConfig) {
onPreviewSource(null);
return;
}
const sourceDescriptor = ESGeoGridSource.createDescriptor(sourceConfig);
const source = new ESGeoGridSource(sourceDescriptor, inspectorAdapters);
onPreviewSource(source);
};
return (
<CreateSourceEditor
requestType={RENDER_AS.HEATMAP}
onSourceConfigChange={onSourceConfigChange}
/>
);
},
title: heatmapTitle,
};

View file

@ -4,4 +4,8 @@
* you may not use this file except in compliance with the Elastic License.
*/
export { ESGeoGridSource } from './es_geo_grid_source';
export {
ESGeoGridSource,
clustersLayerWizardConfig,
heatmapLayerWizardConfig,
} from './es_geo_grid_source';

View file

@ -0,0 +1,59 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import React from 'react';
import { EuiFormRow, EuiComboBox, EuiComboBoxOptionOption } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { RENDER_AS } from '../../../../common/constants';
const options = [
{
label: i18n.translate('xpack.maps.source.esGeoGrid.pointsDropdownOption', {
defaultMessage: 'clusters',
}),
value: RENDER_AS.POINT,
},
{
label: i18n.translate('xpack.maps.source.esGeoGrid.gridRectangleDropdownOption', {
defaultMessage: 'grids',
}),
value: RENDER_AS.GRID,
},
];
export function RenderAsSelect(props: {
renderAs: RENDER_AS;
onChange: (newValue: RENDER_AS) => void;
}) {
function onChange(selectedOptions: Array<EuiComboBoxOptionOption<RENDER_AS>>) {
if (!selectedOptions || !selectedOptions.length) {
return;
}
props.onChange(selectedOptions[0].value as RENDER_AS);
}
const selectedOptions = [];
const selectedOption = options.find(option => option.value === props.renderAs);
if (selectedOption) {
selectedOptions.push(selectedOption);
}
return (
<EuiFormRow
label={i18n.translate('xpack.maps.source.esGeoGrid.showAsLabel', {
defaultMessage: 'Show as',
})}
>
<EuiComboBox
singleSelection={{ asPlainText: true }}
options={options}
selectedOptions={selectedOptions}
onChange={onChange}
isClearable={false}
/>
</EuiFormRow>
);
}

View file

@ -26,17 +26,16 @@ import { AbstractESAggSource } from '../es_agg_source';
import { DynamicStyleProperty } from '../../styles/vector/properties/dynamic_style_property';
import { COLOR_GRADIENTS } from '../../styles/color_utils';
import { indexPatterns } from '../../../../../../../../src/plugins/data/public';
import { registerSource } from '../source_registry';
const MAX_GEOTILE_LEVEL = 29;
const sourceTitle = i18n.translate('xpack.maps.source.pewPewTitle', {
defaultMessage: 'Point to point',
});
export class ESPewPewSource extends AbstractESAggSource {
static type = ES_PEW_PEW;
static title = i18n.translate('xpack.maps.source.pewPewTitle', {
defaultMessage: 'Point to point',
});
static description = i18n.translate('xpack.maps.source.pewPewDescription', {
defaultMessage: 'Aggregated data paths between the source and destination',
});
static createDescriptor({ indexPatternId, sourceGeoField, destGeoField }) {
return {
@ -48,21 +47,6 @@ export class ESPewPewSource extends AbstractESAggSource {
};
}
static renderEditor({ onPreviewSource, inspectorAdapters }) {
const onSourceConfigChange = sourceConfig => {
if (!sourceConfig) {
onPreviewSource(null);
return;
}
const sourceDescriptor = ESPewPewSource.createDescriptor(sourceConfig);
const source = new ESPewPewSource(sourceDescriptor, inspectorAdapters);
onPreviewSource(source);
};
return <CreateSourceEditor onSourceConfigChange={onSourceConfigChange} />;
}
renderSourceSettingsEditor({ onChange }) {
return (
<UpdateSourceEditor
@ -102,7 +86,7 @@ export class ESPewPewSource extends AbstractESAggSource {
return [
{
label: getDataSourceLabel(),
value: ESPewPewSource.title,
value: sourceTitle,
},
{
label: i18n.translate('xpack.maps.source.pewPew.indexPatternLabel', {
@ -245,3 +229,30 @@ export class ESPewPewSource extends AbstractESAggSource {
return await this.filterAndFormatPropertiesToHtmlForMetricFields(properties);
}
}
registerSource({
ConstructorFunction: ESPewPewSource,
type: ES_PEW_PEW,
});
export const point2PointLayerWizardConfig = {
description: i18n.translate('xpack.maps.source.pewPewDescription', {
defaultMessage: 'Aggregated data paths between the source and destination',
}),
icon: 'logoElasticsearch',
renderWizard: ({ onPreviewSource, inspectorAdapters }) => {
const onSourceConfigChange = sourceConfig => {
if (!sourceConfig) {
onPreviewSource(null);
return;
}
const sourceDescriptor = ESPewPewSource.createDescriptor(sourceConfig);
const source = new ESPewPewSource(sourceDescriptor, inspectorAdapters);
onPreviewSource(source);
};
return <CreateSourceEditor onSourceConfigChange={onSourceConfigChange} />;
},
title: sourceTitle,
};

View file

@ -32,6 +32,11 @@ import { BlendedVectorLayer } from '../../blended_vector_layer';
import { DEFAULT_FILTER_BY_MAP_BOUNDS } from './constants';
import { ESDocField } from '../../fields/es_doc_field';
import { getField, addFieldToDSL } from '../../util/es_agg_utils';
import { registerSource } from '../source_registry';
const sourceTitle = i18n.translate('xpack.maps.source.esSearchTitle', {
defaultMessage: 'Documents',
});
function getDocValueAndSourceFields(indexPattern, fieldNames) {
const docValueFields = [];
@ -65,31 +70,6 @@ function getDocValueAndSourceFields(indexPattern, fieldNames) {
export class ESSearchSource extends AbstractESSource {
static type = ES_SEARCH;
static title = i18n.translate('xpack.maps.source.esSearchTitle', {
defaultMessage: 'Documents',
});
static description = i18n.translate('xpack.maps.source.esSearchDescription', {
defaultMessage: 'Vector data from a Kibana index pattern',
});
static renderEditor({ onPreviewSource, inspectorAdapters }) {
const onSourceConfigChange = sourceConfig => {
if (!sourceConfig) {
onPreviewSource(null);
return;
}
const source = new ESSearchSource(
{
id: uuid(),
...sourceConfig,
},
inspectorAdapters
);
onPreviewSource(source);
};
return <CreateSourceEditor onSourceConfigChange={onSourceConfigChange} />;
}
constructor(descriptor, inspectorAdapters) {
super(
@ -206,7 +186,7 @@ export class ESSearchSource extends AbstractESSource {
return [
{
label: getDataSourceLabel(),
value: ESSearchSource.title,
value: sourceTitle,
},
{
label: i18n.translate('xpack.maps.source.esSearch.indexPatternLabel', {
@ -587,3 +567,34 @@ export class ESSearchSource extends AbstractESSource {
};
}
}
registerSource({
ConstructorFunction: ESSearchSource,
type: ES_SEARCH,
});
export const esDocumentsLayerWizardConfig = {
description: i18n.translate('xpack.maps.source.esSearchDescription', {
defaultMessage: 'Vector data from a Kibana index pattern',
}),
icon: 'logoElasticsearch',
renderWizard: ({ onPreviewSource, inspectorAdapters }) => {
const onSourceConfigChange = sourceConfig => {
if (!sourceConfig) {
onPreviewSource(null);
return;
}
const source = new ESSearchSource(
{
id: uuid(),
...sourceConfig,
},
inspectorAdapters
);
onPreviewSource(source);
};
return <CreateSourceEditor onSourceConfigChange={onSourceConfigChange} />;
},
title: sourceTitle,
};

View file

@ -4,4 +4,4 @@
* you may not use this file except in compliance with the Elastic License.
*/
export { ESSearchSource } from './es_search_source';
export { ESSearchSource, esDocumentsLayerWizardConfig } from './es_search_source';

View file

@ -23,8 +23,6 @@ import { DataRequestAbortError } from '../util/data_request';
import { expandToTileBoundaries } from './es_geo_grid_source/geo_tile_utils';
export class AbstractESSource extends AbstractVectorSource {
static icon = 'logoElasticsearch';
constructor(descriptor, inspectorAdapters) {
super(
{

View file

@ -4,4 +4,4 @@
* you may not use this file except in compliance with the Elastic License.
*/
export { KibanaRegionmapSource } from './kibana_regionmap_source';
export { KibanaRegionmapSource, kibanaRegionMapLayerWizardConfig } from './kibana_regionmap_source';

View file

@ -10,18 +10,16 @@ import { CreateSourceEditor } from './create_source_editor';
import { getKibanaRegionList } from '../../../meta';
import { i18n } from '@kbn/i18n';
import { getDataSourceLabel } from '../../../../common/i18n_getters';
import { FIELD_ORIGIN } from '../../../../common/constants';
import { FIELD_ORIGIN, REGIONMAP_FILE } from '../../../../common/constants';
import { KibanaRegionField } from '../../fields/kibana_region_field';
import { registerSource } from '../source_registry';
const sourceTitle = i18n.translate('xpack.maps.source.kbnRegionMapTitle', {
defaultMessage: 'Configured GeoJSON',
});
export class KibanaRegionmapSource extends AbstractVectorSource {
static type = 'REGIONMAP_FILE';
static title = i18n.translate('xpack.maps.source.kbnRegionMapTitle', {
defaultMessage: 'Configured GeoJSON',
});
static description = i18n.translate('xpack.maps.source.kbnRegionMapDescription', {
defaultMessage: 'Vector data from hosted GeoJSON configured in kibana.yml',
});
static icon = 'logoKibana';
static type = REGIONMAP_FILE;
static createDescriptor({ name }) {
return {
@ -30,16 +28,6 @@ export class KibanaRegionmapSource extends AbstractVectorSource {
};
}
static renderEditor = ({ onPreviewSource, inspectorAdapters }) => {
const onSourceConfigChange = sourceConfig => {
const sourceDescriptor = KibanaRegionmapSource.createDescriptor(sourceConfig);
const source = new KibanaRegionmapSource(sourceDescriptor, inspectorAdapters);
onPreviewSource(source);
};
return <CreateSourceEditor onSourceConfigChange={onSourceConfigChange} />;
};
createField({ fieldName }) {
return new KibanaRegionField({
fieldName,
@ -52,7 +40,7 @@ export class KibanaRegionmapSource extends AbstractVectorSource {
return [
{
label: getDataSourceLabel(),
value: KibanaRegionmapSource.title,
value: sourceTitle,
},
{
label: i18n.translate('xpack.maps.source.kbnRegionMap.vectorLayerLabel', {
@ -108,3 +96,25 @@ export class KibanaRegionmapSource extends AbstractVectorSource {
return true;
}
}
registerSource({
ConstructorFunction: KibanaRegionmapSource,
type: REGIONMAP_FILE,
});
export const kibanaRegionMapLayerWizardConfig = {
description: i18n.translate('xpack.maps.source.kbnRegionMapDescription', {
defaultMessage: 'Vector data from hosted GeoJSON configured in kibana.yml',
}),
icon: 'logoKibana',
renderWizard: ({ onPreviewSource, inspectorAdapters }) => {
const onSourceConfigChange = sourceConfig => {
const sourceDescriptor = KibanaRegionmapSource.createDescriptor(sourceConfig);
const source = new KibanaRegionmapSource(sourceDescriptor, inspectorAdapters);
onPreviewSource(source);
};
return <CreateSourceEditor onSourceConfigChange={onSourceConfigChange} />;
},
title: sourceTitle,
};

View file

@ -4,4 +4,4 @@
* you may not use this file except in compliance with the Elastic License.
*/
export { KibanaTilemapSource } from './kibana_tilemap_source';
export { KibanaTilemapSource, kibanaBasemapLayerWizardConfig } from './kibana_tilemap_source';

View file

@ -11,17 +11,15 @@ import { getKibanaTileMap } from '../../../meta';
import { i18n } from '@kbn/i18n';
import { getDataSourceLabel } from '../../../../common/i18n_getters';
import _ from 'lodash';
import { KIBANA_TILEMAP } from '../../../../common/constants';
import { registerSource } from '../source_registry';
const sourceTitle = i18n.translate('xpack.maps.source.kbnTMSTitle', {
defaultMessage: 'Configured Tile Map Service',
});
export class KibanaTilemapSource extends AbstractTMSSource {
static type = 'KIBANA_TILEMAP';
static title = i18n.translate('xpack.maps.source.kbnTMSTitle', {
defaultMessage: 'Configured Tile Map Service',
});
static description = i18n.translate('xpack.maps.source.kbnTMSDescription', {
defaultMessage: 'Tile map service configured in kibana.yml',
});
static icon = 'logoKibana';
static type = KIBANA_TILEMAP;
static createDescriptor() {
return {
@ -29,20 +27,11 @@ export class KibanaTilemapSource extends AbstractTMSSource {
};
}
static renderEditor = ({ onPreviewSource, inspectorAdapters }) => {
const onSourceConfigChange = () => {
const sourceDescriptor = KibanaTilemapSource.createDescriptor();
const source = new KibanaTilemapSource(sourceDescriptor, inspectorAdapters);
onPreviewSource(source);
};
return <CreateSourceEditor onSourceConfigChange={onSourceConfigChange} />;
};
async getImmutableProperties() {
return [
{
label: getDataSourceLabel(),
value: KibanaTilemapSource.title,
value: sourceTitle,
},
{
label: i18n.translate('xpack.maps.source.kbnTMS.urlLabel', {
@ -94,3 +83,24 @@ export class KibanaTilemapSource extends AbstractTMSSource {
}
}
}
registerSource({
ConstructorFunction: KibanaTilemapSource,
type: KIBANA_TILEMAP,
});
export const kibanaBasemapLayerWizardConfig = {
description: i18n.translate('xpack.maps.source.kbnTMSDescription', {
defaultMessage: 'Tile map service configured in kibana.yml',
}),
icon: 'logoKibana',
renderWizard: ({ onPreviewSource, inspectorAdapters }) => {
const onSourceConfigChange = () => {
const sourceDescriptor = KibanaTilemapSource.createDescriptor();
const source = new KibanaTilemapSource(sourceDescriptor, inspectorAdapters);
onPreviewSource(source);
};
return <CreateSourceEditor onSourceConfigChange={onSourceConfigChange} />;
},
title: sourceTitle,
};

View file

@ -0,0 +1,35 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
/* eslint-disable @typescript-eslint/consistent-type-definitions */
import { AbstractSourceDescriptor } from '../../../common/descriptor_types';
import { ISource } from './source';
type SourceRegistryEntry = {
ConstructorFunction: new (
sourceDescriptor: AbstractSourceDescriptor,
inspectorAdapters: unknown
) => ISource;
type: string;
};
const registry: SourceRegistryEntry[] = [];
export function registerSource(entry: SourceRegistryEntry) {
const sourceTypeExists = registry.some(({ type }: SourceRegistryEntry) => {
return entry.type === type;
});
if (sourceTypeExists) {
throw new Error(
`Unable to register source type ${entry.type}. ${entry.type} has already been registered`
);
}
registry.push(entry);
}
export function getSourceByType(sourceType: string): SourceRegistryEntry | undefined {
return registry.find((source: SourceRegistryEntry) => source.type === sourceType);
}

View file

@ -4,4 +4,4 @@
* you may not use this file except in compliance with the Elastic License.
*/
export { WMSSource } from './wms_source';
export { WMSSource, wmsLayerWizardConfig } from './wms_source';

View file

@ -12,16 +12,15 @@ import { WMSCreateSourceEditor } from './wms_create_source_editor';
import { i18n } from '@kbn/i18n';
import { getDataSourceLabel, getUrlLabel } from '../../../../common/i18n_getters';
import { WmsClient } from './wms_client';
import { WMS } from '../../../../common/constants';
import { registerSource } from '../source_registry';
const sourceTitle = i18n.translate('xpack.maps.source.wmsTitle', {
defaultMessage: 'Web Map Service',
});
export class WMSSource extends AbstractTMSSource {
static type = 'WMS';
static title = i18n.translate('xpack.maps.source.wmsTitle', {
defaultMessage: 'Web Map Service',
});
static description = i18n.translate('xpack.maps.source.wmsDescription', {
defaultMessage: 'Maps from OGC Standard WMS',
});
static icon = 'grid';
static type = WMS;
static createDescriptor({ serviceUrl, layers, styles, attributionText, attributionUrl }) {
return {
@ -34,23 +33,9 @@ export class WMSSource extends AbstractTMSSource {
};
}
static renderEditor({ onPreviewSource, inspectorAdapters }) {
const onSourceConfigChange = sourceConfig => {
if (!sourceConfig) {
onPreviewSource(null);
return;
}
const sourceDescriptor = WMSSource.createDescriptor(sourceConfig);
const source = new WMSSource(sourceDescriptor, inspectorAdapters);
onPreviewSource(source);
};
return <WMSCreateSourceEditor onSourceConfigChange={onSourceConfigChange} />;
}
async getImmutableProperties() {
return [
{ label: getDataSourceLabel(), value: WMSSource.title },
{ label: getDataSourceLabel(), value: sourceTitle },
{ label: getUrlLabel(), value: this._descriptor.serviceUrl },
{
label: i18n.translate('xpack.maps.source.wms.layersLabel', {
@ -104,3 +89,29 @@ export class WMSSource extends AbstractTMSSource {
return client.getUrlTemplate(this._descriptor.layers, this._descriptor.styles || '');
}
}
registerSource({
ConstructorFunction: WMSSource,
type: WMS,
});
export const wmsLayerWizardConfig = {
description: i18n.translate('xpack.maps.source.wmsDescription', {
defaultMessage: 'Maps from OGC Standard WMS',
}),
icon: 'grid',
renderWizard: ({ onPreviewSource, inspectorAdapters }) => {
const onSourceConfigChange = sourceConfig => {
if (!sourceConfig) {
onPreviewSource(null);
return;
}
const sourceDescriptor = WMSSource.createDescriptor(sourceConfig);
const source = new WMSSource(sourceDescriptor, inspectorAdapters);
onPreviewSource(source);
};
return <WMSCreateSourceEditor onSourceConfigChange={onSourceConfigChange} />;
},
title: sourceTitle,
};

View file

@ -13,16 +13,14 @@ import { i18n } from '@kbn/i18n';
import { getDataSourceLabel, getUrlLabel } from '../../../common/i18n_getters';
import _ from 'lodash';
import { EMS_XYZ } from '../../../common/constants';
import { registerSource } from './source_registry';
const sourceTitle = i18n.translate('xpack.maps.source.ems_xyzTitle', {
defaultMessage: 'Tile Map Service',
});
export class XYZTMSSource extends AbstractTMSSource {
static type = EMS_XYZ;
static title = i18n.translate('xpack.maps.source.ems_xyzTitle', {
defaultMessage: 'Tile Map Service',
});
static description = i18n.translate('xpack.maps.source.ems_xyzDescription', {
defaultMessage: 'Tile map service configured in interface',
});
static icon = 'grid';
static createDescriptor({ urlTemplate, attributionText, attributionUrl }) {
return {
@ -33,18 +31,9 @@ export class XYZTMSSource extends AbstractTMSSource {
};
}
static renderEditor({ onPreviewSource, inspectorAdapters }) {
const onSourceConfigChange = sourceConfig => {
const sourceDescriptor = XYZTMSSource.createDescriptor(sourceConfig);
const source = new XYZTMSSource(sourceDescriptor, inspectorAdapters);
onPreviewSource(source);
};
return <XYZTMSEditor onSourceConfigChange={onSourceConfigChange} />;
}
async getImmutableProperties() {
return [
{ label: getDataSourceLabel(), value: XYZTMSSource.title },
{ label: getDataSourceLabel(), value: sourceTitle },
{ label: getUrlLabel(), value: this._descriptor.urlTemplate },
];
}
@ -175,3 +164,24 @@ class XYZTMSEditor extends React.Component {
);
}
}
registerSource({
ConstructorFunction: XYZTMSSource,
type: EMS_XYZ,
});
export const tmsLayerWizardConfig = {
description: i18n.translate('xpack.maps.source.ems_xyzDescription', {
defaultMessage: 'Tile map service configured in interface',
}),
icon: 'grid',
renderWizard: ({ onPreviewSource, inspectorAdapters }) => {
const onSourceConfigChange = sourceConfig => {
const sourceDescriptor = XYZTMSSource.createDescriptor(sourceConfig);
const source = new XYZTMSSource(sourceDescriptor, inspectorAdapters);
onPreviewSource(source);
};
return <XYZTMSEditor onSourceConfigChange={onSourceConfigChange} />;
},
title: sourceTitle,
};

View file

@ -4,6 +4,10 @@
* you may not use this file except in compliance with the Elastic License.
*/
import './layers/layer_wizard_registry';
import './layers/sources/source_registry';
import './layers/load_layer_wizards';
import { Plugin, CoreStart, CoreSetup } from 'src/core/public';
// @ts-ignore
import { wrapInI18nContext } from 'ui/i18n';

View file

@ -11,7 +11,6 @@ import { VectorTileLayer } from '../layers/vector_tile_layer';
import { VectorLayer } from '../layers/vector_layer';
import { HeatmapLayer } from '../layers/heatmap_layer';
import { BlendedVectorLayer } from '../layers/blended_vector_layer';
import { ALL_SOURCES } from '../layers/sources/all_sources';
import { getTimeFilter } from '../kibana_services';
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
import { getInspectorAdapters } from '../../../../../plugins/maps/public/reducers/non_serializable_instances';
@ -21,6 +20,7 @@ import {
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
} from '../../../../../plugins/maps/public/reducers/util';
import { InnerJoin } from '../layers/joins/inner_join';
import { getSourceByType } from '../layers/sources/source_registry';
function createLayerInstance(layerDescriptor, inspectorAdapters) {
const source = createSourceInstance(layerDescriptor.sourceDescriptor, inspectorAdapters);
@ -49,13 +49,11 @@ function createLayerInstance(layerDescriptor, inspectorAdapters) {
}
function createSourceInstance(sourceDescriptor, inspectorAdapters) {
const Source = ALL_SOURCES.find(Source => {
return Source.type === sourceDescriptor.type;
});
if (!Source) {
const source = getSourceByType(sourceDescriptor.type);
if (!source) {
throw new Error(`Unrecognized sourceType ${sourceDescriptor.type}`);
}
return new Source(sourceDescriptor, inspectorAdapters);
return new source.ConstructorFunction(sourceDescriptor, inspectorAdapters);
}
export const getOpenTooltips = ({ map }) => {

View file

@ -8,7 +8,6 @@ jest.mock('../layers/vector_layer', () => {});
jest.mock('../layers/blended_vector_layer', () => {});
jest.mock('../layers/heatmap_layer', () => {});
jest.mock('../layers/vector_tile_layer', () => {});
jest.mock('../layers/sources/all_sources', () => {});
jest.mock('../layers/joins/inner_join', () => {});
jest.mock('../../../../../plugins/maps/public/reducers/non_serializable_instances', () => ({
getInspectorAdapters: () => {

View file

@ -62,6 +62,9 @@ export const ES_GEO_GRID = 'ES_GEO_GRID';
export const ES_SEARCH = 'ES_SEARCH';
export const ES_PEW_PEW = 'ES_PEW_PEW';
export const EMS_XYZ = 'EMS_XYZ'; // identifies a custom TMS source. Name is a little unfortunate.
export const WMS = 'WMS';
export const KIBANA_TILEMAP = 'KIBANA_TILEMAP';
export const REGIONMAP_FILE = 'REGIONMAP_FILE';
export enum FIELD_ORIGIN {
SOURCE = 'source',

View file

@ -7137,12 +7137,10 @@
"xpack.maps.source.esGeoGrid.geofieldLabel": "地理空間フィールド",
"xpack.maps.source.esGeoGrid.geofieldPlaceholder": "ジオフィールドを選択",
"xpack.maps.source.esGeoGrid.gridRectangleDropdownOption": "グリッド四角",
"xpack.maps.source.esGeoGrid.heatmapDropdownOption": "ヒートマップ",
"xpack.maps.source.esGeoGrid.indexPatternLabel": "インデックスパターン",
"xpack.maps.source.esGeoGrid.indexPatternPlaceholder": "インデックスパターンを選択",
"xpack.maps.source.esGeoGrid.pointsDropdownOption": "点",
"xpack.maps.source.esGeoGrid.showAsLabel": "表示形式",
"xpack.maps.source.esGeoGrid.showAsPlaceholder": "1 つのオプションを選択",
"xpack.maps.source.esGrid.coarseDropdownOption": "粗い",
"xpack.maps.source.esGrid.fineDropdownOption": "細かい",
"xpack.maps.source.esGrid.finestDropdownOption": "最も細かい",
@ -7151,9 +7149,6 @@
"xpack.maps.source.esGrid.metricsLabel": "メトリック",
"xpack.maps.source.esGrid.noIndexPatternErrorMessage": "インデックスパターン {id} が見つかりません",
"xpack.maps.source.esGrid.resolutionParamErrorMessage": "グリッド解像度パラメーターが認識されません: {resolution}",
"xpack.maps.source.esGrid.showasFieldLabel": "表示形式",
"xpack.maps.source.esGridDescription": "それぞれのグリッド付きセルのメトリックでグリッドにグループ分けされた地理空間データです。",
"xpack.maps.source.esGridTitle": "グリッド集約",
"xpack.maps.source.esSearch.convertToGeoJsonErrorMsg": "検索への応答を geoJson 機能コレクションに変換できません。エラー: {errorMsg}",
"xpack.maps.source.esSearch.extentFilterLabel": "マップの表示範囲でデータを動的にフィルタリング",
"xpack.maps.source.esSearch.geofieldLabel": "地理空間フィールド",

View file

@ -7137,12 +7137,10 @@
"xpack.maps.source.esGeoGrid.geofieldLabel": "地理空间字段",
"xpack.maps.source.esGeoGrid.geofieldPlaceholder": "选择地理字段",
"xpack.maps.source.esGeoGrid.gridRectangleDropdownOption": "网格矩形",
"xpack.maps.source.esGeoGrid.heatmapDropdownOption": "热图",
"xpack.maps.source.esGeoGrid.indexPatternLabel": "索引模式",
"xpack.maps.source.esGeoGrid.indexPatternPlaceholder": "选择索引模式",
"xpack.maps.source.esGeoGrid.pointsDropdownOption": "磅",
"xpack.maps.source.esGeoGrid.showAsLabel": "显示为",
"xpack.maps.source.esGeoGrid.showAsPlaceholder": "选择单个选项",
"xpack.maps.source.esGrid.coarseDropdownOption": "粗糙",
"xpack.maps.source.esGrid.fineDropdownOption": "精致",
"xpack.maps.source.esGrid.finestDropdownOption": "最精致化",
@ -7151,9 +7149,6 @@
"xpack.maps.source.esGrid.metricsLabel": "指标",
"xpack.maps.source.esGrid.noIndexPatternErrorMessage": "找不到索引模式 {id}",
"xpack.maps.source.esGrid.resolutionParamErrorMessage": "无法识别网格分辨率参数:{resolution}",
"xpack.maps.source.esGrid.showasFieldLabel": "显示为",
"xpack.maps.source.esGridDescription": "地理空间数据在网格中进行分组,每个网格单元格都具有指标",
"xpack.maps.source.esGridTitle": "网格聚合",
"xpack.maps.source.esSearch.convertToGeoJsonErrorMsg": "无法将搜索响应转换成 geoJson 功能集合,错误:{errorMsg}",
"xpack.maps.source.esSearch.extentFilterLabel": "在可见地图区域中动态筛留数据",
"xpack.maps.source.esSearch.geofieldLabel": "地理空间字段",

View file

@ -511,7 +511,7 @@ export function GisPageProvider({ getService, getPageObjects }) {
async selectGeoJsonUploadSource() {
log.debug(`Select upload geojson source`);
await testSubjects.click('uploadedGeoJson');
await testSubjects.click('uploadGeoJson');
}
async uploadJsonFileForIndexing(path) {