[Maps] Add draw wizard (#100278)
This commit is contained in:
parent
639df23aa4
commit
c5aa39835d
|
@ -5,16 +5,16 @@
|
||||||
* 2.0.
|
* 2.0.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react';
|
import { lazyLoadModules } from '../lazy_load_bundle';
|
||||||
import { FileUploadComponentProps, lazyLoadModules } from '../lazy_load_bundle';
|
|
||||||
import type { IImporter, ImportFactoryOptions } from '../importer';
|
import type { IImporter, ImportFactoryOptions } from '../importer';
|
||||||
import { IndexNameFormProps } from '../';
|
|
||||||
import type { HasImportPermission, FindFileStructureResponse } from '../../common';
|
import type { HasImportPermission, FindFileStructureResponse } from '../../common';
|
||||||
import type { getMaxBytes, getMaxBytesFormatted } from '../importer/get_max_bytes';
|
import type { getMaxBytes, getMaxBytesFormatted } from '../importer/get_max_bytes';
|
||||||
|
import { JsonUploadAndParseAsyncWrapper } from './json_upload_and_parse_async_wrapper';
|
||||||
|
import { IndexNameFormAsyncWrapper } from './index_name_form_async_wrapper';
|
||||||
|
|
||||||
export interface FileUploadStartApi {
|
export interface FileUploadStartApi {
|
||||||
getFileUploadComponent(): ReturnType<typeof getFileUploadComponent>;
|
FileUploadComponent: typeof JsonUploadAndParseAsyncWrapper;
|
||||||
getIndexNameFormComponent(): Promise<React.ComponentType<IndexNameFormProps>>;
|
IndexNameFormComponent: typeof IndexNameFormAsyncWrapper;
|
||||||
importerFactory: typeof importerFactory;
|
importerFactory: typeof importerFactory;
|
||||||
getMaxBytes: typeof getMaxBytes;
|
getMaxBytes: typeof getMaxBytes;
|
||||||
getMaxBytesFormatted: typeof getMaxBytesFormatted;
|
getMaxBytesFormatted: typeof getMaxBytesFormatted;
|
||||||
|
@ -30,19 +30,8 @@ export interface GetTimeFieldRangeResponse {
|
||||||
end: { epoch: number; string: string };
|
end: { epoch: number; string: string };
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getFileUploadComponent(): Promise<
|
export const FileUploadComponent = JsonUploadAndParseAsyncWrapper;
|
||||||
React.ComponentType<FileUploadComponentProps>
|
export const IndexNameFormComponent = IndexNameFormAsyncWrapper;
|
||||||
> {
|
|
||||||
const fileUploadModules = await lazyLoadModules();
|
|
||||||
return fileUploadModules.JsonUploadAndParse;
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function getIndexNameFormComponent(): Promise<
|
|
||||||
React.ComponentType<IndexNameFormProps>
|
|
||||||
> {
|
|
||||||
const fileUploadModules = await lazyLoadModules();
|
|
||||||
return fileUploadModules.IndexNameForm;
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function importerFactory(
|
export async function importerFactory(
|
||||||
format: string,
|
format: string,
|
||||||
|
|
|
@ -0,0 +1,43 @@
|
||||||
|
/*
|
||||||
|
* 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 { EuiLoadingContent } from '@elastic/eui';
|
||||||
|
import { lazyLoadModules } from '../lazy_load_bundle';
|
||||||
|
import { IndexNameFormProps } from '../index';
|
||||||
|
|
||||||
|
interface State {
|
||||||
|
IndexNameForm: React.ComponentType<IndexNameFormProps> | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class IndexNameFormAsyncWrapper extends React.Component<IndexNameFormProps, State> {
|
||||||
|
state: State = {
|
||||||
|
IndexNameForm: null,
|
||||||
|
};
|
||||||
|
|
||||||
|
private _isMounted = false;
|
||||||
|
|
||||||
|
componentWillUnmount(): void {
|
||||||
|
this._isMounted = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
this._isMounted = true;
|
||||||
|
lazyLoadModules().then((modules) => {
|
||||||
|
if (this._isMounted) {
|
||||||
|
this.setState({
|
||||||
|
IndexNameForm: modules.IndexNameForm,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { IndexNameForm } = this.state;
|
||||||
|
return IndexNameForm ? <IndexNameForm {...this.props} /> : <EuiLoadingContent lines={3} />;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,48 @@
|
||||||
|
/*
|
||||||
|
* 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 { EuiLoadingContent } from '@elastic/eui';
|
||||||
|
import { FileUploadComponentProps, lazyLoadModules } from '../lazy_load_bundle';
|
||||||
|
|
||||||
|
interface State {
|
||||||
|
JsonUploadAndParse: React.ComponentType<FileUploadComponentProps> | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class JsonUploadAndParseAsyncWrapper extends React.Component<
|
||||||
|
FileUploadComponentProps,
|
||||||
|
State
|
||||||
|
> {
|
||||||
|
state: State = {
|
||||||
|
JsonUploadAndParse: null,
|
||||||
|
};
|
||||||
|
private _isMounted = false;
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
this._isMounted = true;
|
||||||
|
lazyLoadModules().then((modules) => {
|
||||||
|
if (this._isMounted) {
|
||||||
|
this.setState({
|
||||||
|
JsonUploadAndParse: modules.JsonUploadAndParse,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillUnmount(): void {
|
||||||
|
this._isMounted = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { JsonUploadAndParse } = this.state;
|
||||||
|
return JsonUploadAndParse ? (
|
||||||
|
<JsonUploadAndParse {...this.props} />
|
||||||
|
) : (
|
||||||
|
<EuiLoadingContent lines={3} />
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -32,7 +32,7 @@ export interface FileUploadComponentProps {
|
||||||
|
|
||||||
let loadModulesPromise: Promise<LazyLoadedFileUploadModules>;
|
let loadModulesPromise: Promise<LazyLoadedFileUploadModules>;
|
||||||
|
|
||||||
interface LazyLoadedFileUploadModules {
|
export interface LazyLoadedFileUploadModules {
|
||||||
JsonUploadAndParse: React.ComponentType<FileUploadComponentProps>;
|
JsonUploadAndParse: React.ComponentType<FileUploadComponentProps>;
|
||||||
IndexNameForm: React.ComponentType<IndexNameFormProps>;
|
IndexNameForm: React.ComponentType<IndexNameFormProps>;
|
||||||
importerFactory: (format: string, options: ImportFactoryOptions) => IImporter | undefined;
|
importerFactory: (format: string, options: ImportFactoryOptions) => IImporter | undefined;
|
||||||
|
|
|
@ -8,10 +8,10 @@
|
||||||
import { CoreStart, Plugin } from '../../../../src/core/public';
|
import { CoreStart, Plugin } from '../../../../src/core/public';
|
||||||
import {
|
import {
|
||||||
FileUploadStartApi,
|
FileUploadStartApi,
|
||||||
getFileUploadComponent,
|
FileUploadComponent,
|
||||||
importerFactory,
|
importerFactory,
|
||||||
hasImportPermission,
|
hasImportPermission,
|
||||||
getIndexNameFormComponent,
|
IndexNameFormComponent,
|
||||||
checkIndexExists,
|
checkIndexExists,
|
||||||
getTimeFieldRange,
|
getTimeFieldRange,
|
||||||
analyzeFile,
|
analyzeFile,
|
||||||
|
@ -42,8 +42,8 @@ export class FileUploadPlugin
|
||||||
public start(core: CoreStart, plugins: FileUploadStartDependencies): FileUploadStartApi {
|
public start(core: CoreStart, plugins: FileUploadStartDependencies): FileUploadStartApi {
|
||||||
setStartServices(core, plugins);
|
setStartServices(core, plugins);
|
||||||
return {
|
return {
|
||||||
getFileUploadComponent,
|
FileUploadComponent,
|
||||||
getIndexNameFormComponent,
|
IndexNameFormComponent,
|
||||||
importerFactory,
|
importerFactory,
|
||||||
getMaxBytes,
|
getMaxBytes,
|
||||||
getMaxBytesFormatted,
|
getMaxBytesFormatted,
|
||||||
|
|
|
@ -41,6 +41,7 @@ export const GIS_API_PATH = `api/${APP_ID}`;
|
||||||
export const INDEX_SETTINGS_API_PATH = `${GIS_API_PATH}/indexSettings`;
|
export const INDEX_SETTINGS_API_PATH = `${GIS_API_PATH}/indexSettings`;
|
||||||
export const FONTS_API_PATH = `${GIS_API_PATH}/fonts`;
|
export const FONTS_API_PATH = `${GIS_API_PATH}/fonts`;
|
||||||
export const INDEX_SOURCE_API_PATH = `${GIS_API_PATH}/docSource`;
|
export const INDEX_SOURCE_API_PATH = `${GIS_API_PATH}/docSource`;
|
||||||
|
export const INDEX_FEATURE_PATH = `/${GIS_API_PATH}/feature`;
|
||||||
export const API_ROOT_PATH = `/${GIS_API_PATH}`;
|
export const API_ROOT_PATH = `/${GIS_API_PATH}`;
|
||||||
|
|
||||||
export const MVT_GETTILE_API_PATH = 'mvt/getTile';
|
export const MVT_GETTILE_API_PATH = 'mvt/getTile';
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export interface CreateDocSourceResp {
|
export interface CreateDocSourceResp {
|
||||||
|
indexPatternId?: string;
|
||||||
success: boolean;
|
success: boolean;
|
||||||
error?: Error;
|
error?: Error;
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,6 @@ import { AnyAction, Dispatch } from 'redux';
|
||||||
import { ThunkDispatch } from 'redux-thunk';
|
import { ThunkDispatch } from 'redux-thunk';
|
||||||
import turfBboxPolygon from '@turf/bbox-polygon';
|
import turfBboxPolygon from '@turf/bbox-polygon';
|
||||||
import turfBooleanContains from '@turf/boolean-contains';
|
import turfBooleanContains from '@turf/boolean-contains';
|
||||||
|
|
||||||
import { Filter, Query, TimeRange } from 'src/plugins/data/public';
|
import { Filter, Query, TimeRange } from 'src/plugins/data/public';
|
||||||
import { MapStoreState } from '../reducers/store';
|
import { MapStoreState } from '../reducers/store';
|
||||||
import {
|
import {
|
||||||
|
|
|
@ -11,13 +11,13 @@ import React, { Component } from 'react';
|
||||||
import { FeatureCollection } from 'geojson';
|
import { FeatureCollection } from 'geojson';
|
||||||
import { EuiPanel } from '@elastic/eui';
|
import { EuiPanel } from '@elastic/eui';
|
||||||
import { DEFAULT_MAX_RESULT_WINDOW, SCALING_TYPES } from '../../../../common/constants';
|
import { DEFAULT_MAX_RESULT_WINDOW, SCALING_TYPES } from '../../../../common/constants';
|
||||||
import { getFileUpload } from '../../../kibana_services';
|
|
||||||
import { GeoJsonFileSource } from '../../sources/geojson_file_source';
|
import { GeoJsonFileSource } from '../../sources/geojson_file_source';
|
||||||
import { VectorLayer } from '../../layers/vector_layer';
|
import { VectorLayer } from '../../layers/vector_layer';
|
||||||
import { createDefaultLayerDescriptor } from '../../sources/es_search_source';
|
import { createDefaultLayerDescriptor } from '../../sources/es_search_source';
|
||||||
import { RenderWizardArguments } from '../../layers/layer_wizard_registry';
|
import { RenderWizardArguments } from '../../layers/layer_wizard_registry';
|
||||||
import { FileUploadComponentProps, FileUploadGeoResults } from '../../../../../file_upload/public';
|
import { FileUploadGeoResults } from '../../../../../file_upload/public';
|
||||||
import { ES_FIELD_TYPES } from '../../../../../../../src/plugins/data/public';
|
import { ES_FIELD_TYPES } from '../../../../../../../src/plugins/data/public';
|
||||||
|
import { getFileUploadComponent } from '../../../kibana_services';
|
||||||
|
|
||||||
export enum UPLOAD_STEPS {
|
export enum UPLOAD_STEPS {
|
||||||
CONFIGURE_UPLOAD = 'CONFIGURE_UPLOAD',
|
CONFIGURE_UPLOAD = 'CONFIGURE_UPLOAD',
|
||||||
|
@ -34,7 +34,6 @@ enum INDEXING_STAGE {
|
||||||
|
|
||||||
interface State {
|
interface State {
|
||||||
indexingStage: INDEXING_STAGE;
|
indexingStage: INDEXING_STAGE;
|
||||||
fileUploadComponent: React.ComponentType<FileUploadComponentProps> | null;
|
|
||||||
results?: FileUploadGeoResults;
|
results?: FileUploadGeoResults;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -43,12 +42,10 @@ export class ClientFileCreateSourceEditor extends Component<RenderWizardArgument
|
||||||
|
|
||||||
state: State = {
|
state: State = {
|
||||||
indexingStage: INDEXING_STAGE.CONFIGURE,
|
indexingStage: INDEXING_STAGE.CONFIGURE,
|
||||||
fileUploadComponent: null,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
this._isMounted = true;
|
this._isMounted = true;
|
||||||
this._loadFileUploadComponent();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
|
@ -91,13 +88,6 @@ export class ClientFileCreateSourceEditor extends Component<RenderWizardArgument
|
||||||
this.props.advanceToNextStep();
|
this.props.advanceToNextStep();
|
||||||
});
|
});
|
||||||
|
|
||||||
async _loadFileUploadComponent() {
|
|
||||||
const fileUploadComponent = await getFileUpload().getFileUploadComponent();
|
|
||||||
if (this._isMounted) {
|
|
||||||
this.setState({ fileUploadComponent });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_onFileSelect = (geojsonFile: FeatureCollection, name: string, previewCoverage: number) => {
|
_onFileSelect = (geojsonFile: FeatureCollection, name: string, previewCoverage: number) => {
|
||||||
if (!this._isMounted) {
|
if (!this._isMounted) {
|
||||||
return;
|
return;
|
||||||
|
@ -157,11 +147,8 @@ export class ClientFileCreateSourceEditor extends Component<RenderWizardArgument
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
if (!this.state.fileUploadComponent) {
|
const FileUpload = getFileUploadComponent();
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const FileUpload = this.state.fileUploadComponent;
|
|
||||||
return (
|
return (
|
||||||
<EuiPanel>
|
<EuiPanel>
|
||||||
<FileUpload
|
<FileUpload
|
||||||
|
|
|
@ -0,0 +1,37 @@
|
||||||
|
/*
|
||||||
|
* 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, { FunctionComponent } from 'react';
|
||||||
|
|
||||||
|
export const DrawLayerIcon: FunctionComponent = () => (
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="49"
|
||||||
|
height="25"
|
||||||
|
fill="none"
|
||||||
|
viewBox="0 0 49 25"
|
||||||
|
className="mapLayersWizardIcon"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
className="mapLayersWizardIcon__background"
|
||||||
|
d="M12.281 3l-6.625 7.625 1.657 8.938 35.218-.813v-13l-10.625-3.5-9.781 9.5L12.281 3z"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
className="mapLayersWizardIcon__highlight"
|
||||||
|
fillRule="evenodd"
|
||||||
|
d="M31.775 1.68l11.256 3.708v13.85l-36.133.834-1.777-9.593 7.114-8.189 9.875 8.778 9.665-9.388zm.262 1.14l-9.897 9.612-9.813-8.722-6.135 7.06 1.535 8.283 34.304-.792V6.111L32.037 2.82z"
|
||||||
|
clipRule="evenodd"
|
||||||
|
/>
|
||||||
|
<circle cx="7.281" cy="19.5" r="2.5" className="mapLayersWizardIcon__highlight" />
|
||||||
|
<circle cx="5.656" cy="10.25" r="2.5" className="mapLayersWizardIcon__highlight" />
|
||||||
|
<circle cx="12.156" cy="3.625" r="2.5" className="mapLayersWizardIcon__highlight" />
|
||||||
|
<circle cx="22" cy="11.6" r="2.5" className="mapLayersWizardIcon__highlight" />
|
||||||
|
<circle cx="31.969" cy="2.5" r="2.5" className="mapLayersWizardIcon__highlight" />
|
||||||
|
<circle cx="42.344" cy="6.125" r="2.5" className="mapLayersWizardIcon__highlight" />
|
||||||
|
<circle cx="42.344" cy="19" r="2.5" className="mapLayersWizardIcon__highlight" />
|
||||||
|
</svg>
|
||||||
|
);
|
|
@ -30,6 +30,8 @@ import { mvtVectorSourceWizardConfig } from '../sources/mvt_single_layer_vector_
|
||||||
import { ObservabilityLayerWizardConfig } from './solution_layers/observability';
|
import { ObservabilityLayerWizardConfig } from './solution_layers/observability';
|
||||||
import { SecurityLayerWizardConfig } from './solution_layers/security';
|
import { SecurityLayerWizardConfig } from './solution_layers/security';
|
||||||
import { choroplethLayerWizardConfig } from './choropleth_layer_wizard';
|
import { choroplethLayerWizardConfig } from './choropleth_layer_wizard';
|
||||||
|
import { newVectorLayerWizardConfig } from './new_vector_layer_wizard';
|
||||||
|
import { getMapAppConfig } from '../../kibana_services';
|
||||||
|
|
||||||
let registered = false;
|
let registered = false;
|
||||||
export function registerLayerWizards() {
|
export function registerLayerWizards() {
|
||||||
|
@ -39,6 +41,9 @@ export function registerLayerWizards() {
|
||||||
|
|
||||||
// Registration order determines display order
|
// Registration order determines display order
|
||||||
registerLayerWizard(uploadLayerWizardConfig);
|
registerLayerWizard(uploadLayerWizardConfig);
|
||||||
|
if (getMapAppConfig().enableDrawingFeature) {
|
||||||
|
registerLayerWizard(newVectorLayerWizardConfig);
|
||||||
|
}
|
||||||
registerLayerWizard(esDocumentsLayerWizardConfig);
|
registerLayerWizard(esDocumentsLayerWizardConfig);
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
registerLayerWizard(choroplethLayerWizardConfig);
|
registerLayerWizard(choroplethLayerWizardConfig);
|
||||||
|
|
|
@ -0,0 +1,49 @@
|
||||||
|
/*
|
||||||
|
* 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 React from 'react';
|
||||||
|
import { LayerWizard, RenderWizardArguments } from '../../layers/layer_wizard_registry';
|
||||||
|
import { NewVectorLayerEditor } from './wizard';
|
||||||
|
import { DrawLayerIcon } from '../../layers/icons/draw_layer_icon';
|
||||||
|
import { getFileUpload } from '../../../kibana_services';
|
||||||
|
import { LAYER_WIZARD_CATEGORY } from '../../../../common';
|
||||||
|
|
||||||
|
const ADD_VECTOR_DRAWING_LAYER = 'ADD_VECTOR_DRAWING_LAYER';
|
||||||
|
|
||||||
|
export const newVectorLayerWizardConfig: LayerWizard = {
|
||||||
|
categories: [LAYER_WIZARD_CATEGORY.ELASTICSEARCH],
|
||||||
|
description: i18n.translate('xpack.maps.newVectorLayerWizard.description', {
|
||||||
|
defaultMessage: 'Creates a new empty layer. Use this to add shapes to the map',
|
||||||
|
}),
|
||||||
|
disabledReason: i18n.translate('xpack.maps.newVectorLayerWizard.disabledDesc', {
|
||||||
|
defaultMessage:
|
||||||
|
'Unable to draw vector shapes, you are missing the Kibana privilege "Index Pattern Management".',
|
||||||
|
}),
|
||||||
|
getIsDisabled: async () => {
|
||||||
|
const hasImportPermission = await getFileUpload().hasImportPermission({
|
||||||
|
checkCreateIndexPattern: true,
|
||||||
|
checkHasManagePipeline: false,
|
||||||
|
});
|
||||||
|
return !hasImportPermission;
|
||||||
|
},
|
||||||
|
icon: DrawLayerIcon,
|
||||||
|
prerequisiteSteps: [
|
||||||
|
{
|
||||||
|
id: ADD_VECTOR_DRAWING_LAYER,
|
||||||
|
label: i18n.translate('xpack.maps.newVectorLayerWizard.indexNewLayer', {
|
||||||
|
defaultMessage: 'Index new layer',
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
renderWizard: (renderWizardArguments: RenderWizardArguments) => {
|
||||||
|
return <NewVectorLayerEditor {...renderWizardArguments} />;
|
||||||
|
},
|
||||||
|
title: i18n.translate('xpack.maps.newVectorLayerWizard.title', {
|
||||||
|
defaultMessage: 'Create new layer',
|
||||||
|
}),
|
||||||
|
};
|
|
@ -0,0 +1,27 @@
|
||||||
|
/*
|
||||||
|
* 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 { getHttp } from '../../../kibana_services';
|
||||||
|
import { CreateDocSourceResp, INDEX_SOURCE_API_PATH } from '../../../../common';
|
||||||
|
|
||||||
|
export const createNewIndexAndPattern = async (indexName: string) => {
|
||||||
|
return await getHttp().fetch<CreateDocSourceResp>({
|
||||||
|
path: `/${INDEX_SOURCE_API_PATH}`,
|
||||||
|
method: 'POST',
|
||||||
|
body: JSON.stringify({
|
||||||
|
index: indexName,
|
||||||
|
// Initially set to static mappings
|
||||||
|
mappings: {
|
||||||
|
properties: {
|
||||||
|
coordinates: {
|
||||||
|
type: 'geo_shape',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
};
|
|
@ -0,0 +1,8 @@
|
||||||
|
/*
|
||||||
|
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||||
|
* or more contributor license agreements. Licensed under the Elastic License
|
||||||
|
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||||
|
* 2.0.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export { newVectorLayerWizardConfig } from './config';
|
|
@ -0,0 +1,161 @@
|
||||||
|
/*
|
||||||
|
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||||
|
* or more contributor license agreements. Licensed under the Elastic License
|
||||||
|
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||||
|
* 2.0.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import React, { Component, Fragment } from 'react';
|
||||||
|
import { EuiEmptyPrompt, EuiPanel, EuiCallOut } from '@elastic/eui';
|
||||||
|
import { i18n } from '@kbn/i18n';
|
||||||
|
import { createNewIndexAndPattern } from './create_new_index_pattern';
|
||||||
|
import { RenderWizardArguments } from '../layer_wizard_registry';
|
||||||
|
import { VectorLayer } from '../vector_layer';
|
||||||
|
import { ESSearchSource } from '../../sources/es_search_source';
|
||||||
|
import { ADD_LAYER_STEP_ID } from '../../../connected_components/add_layer_panel/view';
|
||||||
|
import { getIndexNameFormComponent } from '../../../kibana_services';
|
||||||
|
|
||||||
|
interface State {
|
||||||
|
indexName: string;
|
||||||
|
indexNameError: string;
|
||||||
|
indexingTriggered: boolean;
|
||||||
|
createIndexError: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class NewVectorLayerEditor extends Component<RenderWizardArguments, State> {
|
||||||
|
private _isMounted: boolean = false;
|
||||||
|
|
||||||
|
state: State = {
|
||||||
|
indexName: '',
|
||||||
|
indexNameError: '',
|
||||||
|
indexingTriggered: false,
|
||||||
|
createIndexError: '',
|
||||||
|
};
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
this._isMounted = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
this._isMounted = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
async componentDidUpdate() {
|
||||||
|
if (this.props.currentStepId === ADD_LAYER_STEP_ID && !this.state.indexingTriggered) {
|
||||||
|
this.setState({ indexingTriggered: true });
|
||||||
|
await this._createNewIndex();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_setCreateIndexError(errorMessage: string) {
|
||||||
|
if (!this._isMounted) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.setState({
|
||||||
|
createIndexError: errorMessage,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
_createNewIndex = async () => {
|
||||||
|
let indexPatternId: string | undefined;
|
||||||
|
try {
|
||||||
|
const response = await createNewIndexAndPattern(this.state.indexName);
|
||||||
|
indexPatternId = response.indexPatternId;
|
||||||
|
} catch (e) {
|
||||||
|
this._setCreateIndexError(e.message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!indexPatternId) {
|
||||||
|
this._setCreateIndexError(
|
||||||
|
i18n.translate('xpack.maps.layers.newVectorLayerWizard.createIndexError', {
|
||||||
|
defaultMessage: 'Could not create index with name {message}',
|
||||||
|
values: {
|
||||||
|
message: this.state.indexName,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this._isMounted) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Creates empty layer
|
||||||
|
const sourceDescriptor = ESSearchSource.createDescriptor({
|
||||||
|
indexPatternId,
|
||||||
|
geoField: 'coordinates',
|
||||||
|
filterByMapBounds: false,
|
||||||
|
});
|
||||||
|
const layerDescriptor = VectorLayer.createDescriptor(
|
||||||
|
{ sourceDescriptor },
|
||||||
|
this.props.mapColors
|
||||||
|
);
|
||||||
|
this.props.previewLayers([layerDescriptor]);
|
||||||
|
this.props.advanceToNextStep();
|
||||||
|
};
|
||||||
|
|
||||||
|
_onIndexChange = (indexName: string, indexError?: string) => {
|
||||||
|
this.setState({
|
||||||
|
indexName,
|
||||||
|
indexNameError: indexError ? indexError : '',
|
||||||
|
});
|
||||||
|
if (indexName && !indexError) {
|
||||||
|
this.props.enableNextBtn();
|
||||||
|
} else {
|
||||||
|
this.props.disableNextBtn();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
render() {
|
||||||
|
if (this.state.createIndexError) {
|
||||||
|
return (
|
||||||
|
<EuiCallOut
|
||||||
|
title={i18n.translate('xpack.maps.layers.newVectorLayerWizard.createIndexErrorTitle', {
|
||||||
|
defaultMessage: 'Sorry, could not create index pattern',
|
||||||
|
})}
|
||||||
|
color="danger"
|
||||||
|
iconType="alert"
|
||||||
|
>
|
||||||
|
<p>{this.state.createIndexError}</p>
|
||||||
|
</EuiCallOut>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const IndexNameForm = getIndexNameFormComponent();
|
||||||
|
return (
|
||||||
|
<EuiPanel>
|
||||||
|
<>
|
||||||
|
<EuiEmptyPrompt
|
||||||
|
title={
|
||||||
|
<h4>
|
||||||
|
{i18n.translate('xpack.maps.layers.newVectorLayerWizard.createNewLayer', {
|
||||||
|
defaultMessage: 'Create new layer',
|
||||||
|
})}
|
||||||
|
</h4>
|
||||||
|
}
|
||||||
|
body={
|
||||||
|
<Fragment>
|
||||||
|
<p>
|
||||||
|
{i18n.translate(
|
||||||
|
'xpack.maps.layers.newVectorLayerWizard.vectorEditorDescription',
|
||||||
|
{
|
||||||
|
defaultMessage: `Creates a new vector layer. This can be used to draw and store new shapes.`,
|
||||||
|
}
|
||||||
|
)}
|
||||||
|
</p>
|
||||||
|
</Fragment>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<IndexNameForm
|
||||||
|
indexName={this.state.indexName}
|
||||||
|
indexNameError={this.state.indexNameError}
|
||||||
|
onIndexNameChange={this._onIndexChange}
|
||||||
|
onIndexNameValidationStart={() => {}}
|
||||||
|
onIndexNameValidationEnd={() => {}}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
</EuiPanel>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -21,7 +21,7 @@ import { FlyoutBody } from './flyout_body';
|
||||||
import { LayerDescriptor } from '../../../common/descriptor_types';
|
import { LayerDescriptor } from '../../../common/descriptor_types';
|
||||||
import { LayerWizard } from '../../classes/layers/layer_wizard_registry';
|
import { LayerWizard } from '../../classes/layers/layer_wizard_registry';
|
||||||
|
|
||||||
const ADD_LAYER_STEP_ID = 'ADD_LAYER_STEP_ID';
|
export const ADD_LAYER_STEP_ID = 'ADD_LAYER_STEP_ID';
|
||||||
const ADD_LAYER_STEP_LABEL = i18n.translate('xpack.maps.addLayerPanel.addLayer', {
|
const ADD_LAYER_STEP_LABEL = i18n.translate('xpack.maps.addLayerPanel.addLayer', {
|
||||||
defaultMessage: 'Add layer',
|
defaultMessage: 'Add layer',
|
||||||
});
|
});
|
||||||
|
|
|
@ -418,11 +418,11 @@ export class MBMap extends Component<Props, State> {
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
let drawControl;
|
let drawFilterControl;
|
||||||
let tooltipControl;
|
let tooltipControl;
|
||||||
let scaleControl;
|
let scaleControl;
|
||||||
if (this.state.mbMap) {
|
if (this.state.mbMap) {
|
||||||
drawControl = this.props.addFilters ? (
|
drawFilterControl = this.props.addFilters ? (
|
||||||
<DrawFilterControl mbMap={this.state.mbMap} addFilters={this.props.addFilters} />
|
<DrawFilterControl mbMap={this.state.mbMap} addFilters={this.props.addFilters} />
|
||||||
) : null;
|
) : null;
|
||||||
tooltipControl = !this.props.settings.disableTooltipControl ? (
|
tooltipControl = !this.props.settings.disableTooltipControl ? (
|
||||||
|
@ -447,7 +447,7 @@ export class MBMap extends Component<Props, State> {
|
||||||
ref={this._setContainerRef}
|
ref={this._setContainerRef}
|
||||||
data-test-subj="mapContainer"
|
data-test-subj="mapContainer"
|
||||||
>
|
>
|
||||||
{drawControl}
|
{drawFilterControl}
|
||||||
{scaleControl}
|
{scaleControl}
|
||||||
{tooltipControl}
|
{tooltipControl}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
/*
|
||||||
|
* 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, { FunctionComponent } from 'react';
|
||||||
|
|
||||||
|
export const VectorCircleIcon: FunctionComponent = () => (
|
||||||
|
<svg
|
||||||
|
width="16"
|
||||||
|
height="16"
|
||||||
|
viewBox="0 0 16 16"
|
||||||
|
fill="none"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
className="euiIcon"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
fillRule="evenodd"
|
||||||
|
clipRule="evenodd"
|
||||||
|
d="M8 15C11.866 15 15 11.866 15 8C15 4.13401 11.866 1 8 1C4.13401 1 1 4.13401 1 8C1 11.866 4.13401 15 8 15ZM8 14C11.3137 14 14 11.3137 14 8C14 4.68629 11.3137 2 8 2C4.68629 2 2 4.68629 2 8C2 11.3137 4.68629 14 8 14Z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
);
|
|
@ -0,0 +1,26 @@
|
||||||
|
/*
|
||||||
|
* 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, { FunctionComponent } from 'react';
|
||||||
|
|
||||||
|
export const VectorLineIcon: FunctionComponent = () => (
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="16"
|
||||||
|
height="16"
|
||||||
|
fill="none"
|
||||||
|
viewBox="0 0 16 16"
|
||||||
|
className="euiIcon"
|
||||||
|
>
|
||||||
|
<path d="M11.506 3.881l.707.707-7.594 7.594-.707-.707z" />
|
||||||
|
<path
|
||||||
|
fillRule="evenodd"
|
||||||
|
d="M5 11H1v4h4v-4zm-1 1H2v2h2v-2zM15 1h-4v4h4V1zm-1 1h-2v2h2V2z"
|
||||||
|
clipRule="evenodd"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
);
|
|
@ -0,0 +1,25 @@
|
||||||
|
/*
|
||||||
|
* 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, { FunctionComponent } from 'react';
|
||||||
|
|
||||||
|
export const VectorSquareIcon: FunctionComponent = () => (
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="16"
|
||||||
|
height="16"
|
||||||
|
fill="none"
|
||||||
|
viewBox="0 0 16 16"
|
||||||
|
className="euiIcon"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
fillRule="evenodd"
|
||||||
|
d="M1.5 2a.5.5 0 01.5-.5h12a.5.5 0 01.5.5v12a.5.5 0 01-.5.5H2a.5.5 0 01-.5-.5V2zm1 .5v11h11v-11h-11z"
|
||||||
|
clipRule="evenodd"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
);
|
|
@ -22,6 +22,9 @@ export function setStartServices(core: CoreStart, plugins: MapsPluginStartDepend
|
||||||
coreStart = core;
|
coreStart = core;
|
||||||
pluginsStart = plugins;
|
pluginsStart = plugins;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const getIndexNameFormComponent = () => pluginsStart.fileUpload.IndexNameFormComponent;
|
||||||
|
export const getFileUploadComponent = () => pluginsStart.fileUpload.FileUploadComponent;
|
||||||
export const getIndexPatternService = () => pluginsStart.data.indexPatterns;
|
export const getIndexPatternService = () => pluginsStart.data.indexPatterns;
|
||||||
export const getAutocompleteService = () => pluginsStart.data.autocomplete;
|
export const getAutocompleteService = () => pluginsStart.data.autocomplete;
|
||||||
export const getInspector = () => pluginsStart.inspector;
|
export const getInspector = () => pluginsStart.inspector;
|
||||||
|
@ -55,7 +58,6 @@ let mapAppConfig: MapsConfigType;
|
||||||
export const setMapAppConfig = (config: MapsConfigType) => (mapAppConfig = config);
|
export const setMapAppConfig = (config: MapsConfigType) => (mapAppConfig = config);
|
||||||
export const getMapAppConfig = () => mapAppConfig;
|
export const getMapAppConfig = () => mapAppConfig;
|
||||||
|
|
||||||
export const getEnabled = () => getMapAppConfig().enabled;
|
|
||||||
export const getShowMapsInspectorAdapter = () => getMapAppConfig().showMapsInspectorAdapter;
|
export const getShowMapsInspectorAdapter = () => getMapAppConfig().showMapsInspectorAdapter;
|
||||||
export const getPreserveDrawingBuffer = () => getMapAppConfig().preserveDrawingBuffer;
|
export const getPreserveDrawingBuffer = () => getMapAppConfig().preserveDrawingBuffer;
|
||||||
|
|
||||||
|
|
|
@ -29,9 +29,10 @@ export async function createDocSource(
|
||||||
): Promise<CreateDocSourceResp> {
|
): Promise<CreateDocSourceResp> {
|
||||||
try {
|
try {
|
||||||
await createIndex(index, mappings, asCurrentUser);
|
await createIndex(index, mappings, asCurrentUser);
|
||||||
await indexPatternsService.createAndSave({ title: index }, true);
|
const { id: indexPatternId } = await indexPatternsService.createAndSave({ title: index }, true);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
indexPatternId,
|
||||||
success: true,
|
success: true,
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|
|
@ -11,8 +11,8 @@ import { IRouter } from 'src/core/server';
|
||||||
import type { DataRequestHandlerContext } from 'src/plugins/data/server';
|
import type { DataRequestHandlerContext } from 'src/plugins/data/server';
|
||||||
import {
|
import {
|
||||||
INDEX_SOURCE_API_PATH,
|
INDEX_SOURCE_API_PATH,
|
||||||
GIS_API_PATH,
|
|
||||||
MAX_DRAWING_SIZE_BYTES,
|
MAX_DRAWING_SIZE_BYTES,
|
||||||
|
INDEX_FEATURE_PATH,
|
||||||
} from '../../common/constants';
|
} from '../../common/constants';
|
||||||
import { createDocSource } from './create_doc_source';
|
import { createDocSource } from './create_doc_source';
|
||||||
import { writeDataToIndex } from './index_data';
|
import { writeDataToIndex } from './index_data';
|
||||||
|
@ -70,7 +70,7 @@ export function initIndexingRoutes({
|
||||||
|
|
||||||
router.post(
|
router.post(
|
||||||
{
|
{
|
||||||
path: `/${GIS_API_PATH}/feature`,
|
path: INDEX_FEATURE_PATH,
|
||||||
validate: {
|
validate: {
|
||||||
body: schema.object({
|
body: schema.object({
|
||||||
index: schema.string(),
|
index: schema.string(),
|
||||||
|
|
|
@ -16,6 +16,7 @@ export const config: PluginConfigDescriptor<MapsXPackConfig> = {
|
||||||
exposeToBrowser: {
|
exposeToBrowser: {
|
||||||
enabled: true,
|
enabled: true,
|
||||||
showMapVisualizationTypes: true,
|
showMapVisualizationTypes: true,
|
||||||
|
enableDrawingFeature: true,
|
||||||
showMapsInspectorAdapter: true,
|
showMapsInspectorAdapter: true,
|
||||||
preserveDrawingBuffer: true,
|
preserveDrawingBuffer: true,
|
||||||
},
|
},
|
||||||
|
|
Loading…
Reference in a new issue