[Maps] Cleanup sources (#63175)

- Introduces additional TS typing for sources
- Organizes sources in sub-directories by type
- migrates XYZTMSSource to TS
This commit is contained in:
Thomas Neirynck 2020-04-13 09:24:16 -04:00 committed by GitHub
parent 36b4ad9888
commit bbd501ea51
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
34 changed files with 392 additions and 270 deletions

View file

@ -87,6 +87,8 @@ export function buildMapsTelemetry({
const mapsCount = layerLists.length;
const dataSourcesCount = layerLists.map(lList => {
// todo: not every source-descriptor has an id
// @ts-ignore
const sourceIdList = lList.map((layer: LayerDescriptor) => layer.sourceDescriptor.id);
return _.uniq(sourceIdList).length;
});

View file

@ -9,6 +9,11 @@ import { AGG_TYPE, GRID_RESOLUTION, RENDER_AS, SORT_ORDER, SCALING_TYPES } from
import { VectorStyleDescriptor } from './style_property_descriptor_types';
import { DataRequestDescriptor } from './data_request_descriptor_types';
export type AttributionDescriptor = {
attributionText?: string;
attributionUrl?: string;
};
export type AbstractSourceDescriptor = {
id?: string;
type: string;
@ -84,17 +89,26 @@ export type WMSSourceDescriptor = {
attributionUrl: string;
};
export type XYZTMSSourceDescriptor = {
id: string;
type: string;
urlTemplate: string;
};
export type XYZTMSSourceDescriptor = AbstractSourceDescriptor &
AttributionDescriptor & {
urlTemplate: string;
};
export type JoinDescriptor = {
leftField: string;
right: ESTermSourceDescriptor;
};
export type SourceDescriptor =
| XYZTMSSourceDescriptor
| WMSSourceDescriptor
| KibanaTilemapSourceDescriptor
| KibanaRegionmapSourceDescriptor
| ESTermSourceDescriptor
| ESSearchSourceDescriptor
| ESGeoGridSourceDescriptor
| EMSFileSourceDescriptor;
export type LayerDescriptor = {
__dataRequests?: DataRequestDescriptor[];
__isInErrorState?: boolean;
@ -104,7 +118,7 @@ export type LayerDescriptor = {
label?: string;
minZoom?: number;
maxZoom?: number;
sourceDescriptor: AbstractSourceDescriptor;
sourceDescriptor: SourceDescriptor;
type?: string;
visible?: boolean;
};

View file

@ -34,6 +34,7 @@ import {
VectorStyleDescriptor,
SizeDynamicOptions,
DynamicStylePropertyOptions,
VectorLayerDescriptor,
} from '../../common/descriptor_types';
const ACTIVE_COUNT_DATA_ID = 'ACTIVE_COUNT_DATA_ID';
@ -147,7 +148,10 @@ function getClusterStyleDescriptor(
export class BlendedVectorLayer extends VectorLayer implements IVectorLayer {
static type = LAYER_TYPE.BLENDED_VECTOR;
static createDescriptor(options: VectorLayerArguments, mapColors: string[]) {
static createDescriptor(
options: VectorLayerDescriptor,
mapColors: string[]
): VectorLayerDescriptor {
const layerDescriptor = VectorLayer.createDescriptor(options, mapColors);
layerDescriptor.type = BlendedVectorLayer.type;
return layerDescriptor;

View file

@ -25,6 +25,7 @@ export interface ILayerArguments {
}
export class AbstractLayer implements ILayer {
static createDescriptor(options: Partial<LayerDescriptor>, mapColors?: string[]): LayerDescriptor;
constructor(layerArguments: ILayerArguments);
getBounds(mapFilters: MapFilters): Promise<MapExtent>;
getDataRequest(id: string): DataRequest | undefined;

View file

@ -5,17 +5,21 @@
*/
/* eslint-disable @typescript-eslint/consistent-type-definitions */
type LayerWizard = {
import { ReactElement } from 'react';
import { ISource } from './sources/source';
export type PreviewSourceHandler = (source: ISource | null) => void;
export type RenderWizardArguments = {
onPreviewSource: PreviewSourceHandler;
inspectorAdapters: object;
};
export type LayerWizard = {
description: string;
icon: string;
isIndexingSource?: boolean;
renderWizard({
onPreviewSource,
inspectorAdapters,
}: {
onPreviewSource: () => void;
inspectorAdapters: unknown;
}): unknown;
renderWizard(renderWizardArguments: RenderWizardArguments): ReactElement<any>;
title: string;
};

View file

@ -4,11 +4,11 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { IESSource } from './es_source';
import { AbstractESSource } from './es_source';
import { AGG_TYPE } from '../../../common/constants';
import { IESAggField } from '../fields/es_agg_field';
import { AbstractESAggSourceDescriptor } from '../../../common/descriptor_types';
import { IESSource } from '../es_source';
import { AbstractESSource } from '../es_source';
import { AGG_TYPE } from '../../../../common/constants';
import { IESAggField } from '../../fields/es_agg_field';
import { AbstractESAggSourceDescriptor } from '../../../../common/descriptor_types';
export interface IESAggSource extends IESSource {
getAggKey(aggType: AGG_TYPE, fieldName: string): string;

View file

@ -5,15 +5,15 @@
*/
import { i18n } from '@kbn/i18n';
import { AbstractESSource } from './es_source';
import { esAggFieldsFactory } from '../fields/es_agg_field';
import { AbstractESSource } from '../es_source';
import { esAggFieldsFactory } from '../../fields/es_agg_field';
import {
AGG_TYPE,
COUNT_PROP_LABEL,
COUNT_PROP_NAME,
FIELD_ORIGIN,
} from '../../../common/constants';
} from '../../../../common/constants';
export const AGG_DELIMITER = '_of_';

View file

@ -4,12 +4,12 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { AbstractESAggSource } from './es_agg_source';
import { IField } from '../fields/field';
import { IESAggField } from '../fields/es_agg_field';
import { AbstractESAggSource } from '../es_agg_source';
import { IField } from '../../fields/field';
import { IESAggField } from '../../fields/es_agg_field';
import _ from 'lodash';
import { AGG_TYPE } from '../../../common/constants';
import { AggDescriptor } from '../../../common/descriptor_types';
import { AGG_TYPE } from '../../../../common/constants';
import { AggDescriptor } from '../../../../common/descriptor_types';
jest.mock('ui/new_platform');

View file

@ -0,0 +1,7 @@
/*
* 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.
*/
export * from './es_agg_source';

View file

@ -4,10 +4,10 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { AbstractVectorSource } from './vector_source';
import { IVectorSource } from './vector_source';
import { IndexPattern, SearchSource } from '../../../../../../src/plugins/data/public';
import { VectorSourceRequestMeta } from '../../../common/descriptor_types';
import { AbstractVectorSource } from '../vector_source';
import { IVectorSource } from '../vector_source';
import { IndexPattern, SearchSource } from '../../../../../../../src/plugins/data/public';
import { VectorSourceRequestMeta } from '../../../../common/descriptor_types';
export interface IESSource extends IVectorSource {
getId(): string;

View file

@ -4,23 +4,23 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { AbstractVectorSource } from './vector_source';
import { AbstractVectorSource } from '../vector_source';
import {
getAutocompleteService,
fetchSearchSourceAndRecordWithInspector,
getIndexPatternService,
SearchSource,
getTimeFilter,
} from '../../kibana_services';
import { createExtentFilter } from '../../elasticsearch_geo_utils';
} from '../../../kibana_services';
import { createExtentFilter } from '../../../elasticsearch_geo_utils';
import _ from 'lodash';
import { i18n } from '@kbn/i18n';
import uuid from 'uuid/v4';
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
import { copyPersistentState } from '../../reducers/util';
import { ES_GEO_FIELD_TYPE } from '../../../common/constants';
import { DataRequestAbortError } from '../util/data_request';
import { expandToTileBoundaries } from './es_geo_grid_source/geo_tile_utils';
import { copyPersistentState } from '../../../reducers/util';
import { ES_GEO_FIELD_TYPE } from '../../../../common/constants';
import { DataRequestAbortError } from '../../util/data_request';
import { expandToTileBoundaries } from '../es_geo_grid_source/geo_tile_utils';
export class AbstractESSource extends AbstractVectorSource {
constructor(descriptor, inspectorAdapters) {

View file

@ -0,0 +1,7 @@
/*
* 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.
*/
export * from './es_source';

View file

@ -4,8 +4,8 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { IField } from '../fields/field';
import { IESAggSource } from './es_agg_source';
import { IField } from '../../fields/field';
import { IESAggSource } from '../es_agg_source';
export interface IESTermSource extends IESAggSource {
getTermField(): IField;

View file

@ -7,10 +7,10 @@
import _ from 'lodash';
import { i18n } from '@kbn/i18n';
import { DEFAULT_MAX_BUCKETS_LIMIT, FIELD_ORIGIN, AGG_TYPE } from '../../../common/constants';
import { ESDocField } from '../fields/es_doc_field';
import { AbstractESAggSource, AGG_DELIMITER } from './es_agg_source';
import { getField, addFieldToDSL, extractPropertiesFromBucket } from '../util/es_agg_utils';
import { DEFAULT_MAX_BUCKETS_LIMIT, FIELD_ORIGIN, AGG_TYPE } from '../../../../common/constants';
import { ESDocField } from '../../fields/es_doc_field';
import { AbstractESAggSource, AGG_DELIMITER } from '../es_agg_source';
import { getField, addFieldToDSL, extractPropertiesFromBucket } from '../../util/es_agg_utils';
const TERMS_AGG_NAME = 'join';

View file

@ -7,7 +7,7 @@
import { ESTermSource, extractPropertiesMap } from './es_term_source';
jest.mock('ui/new_platform');
jest.mock('../vector_layer', () => {});
jest.mock('../../vector_layer', () => {});
const indexPatternTitle = 'myIndex';
const termFieldName = 'myTermField';

View file

@ -0,0 +1,7 @@
/*
* 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.
*/
export * from './es_term_source';

View file

@ -3,10 +3,21 @@
* 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 { AbstractSourceDescriptor, LayerDescriptor } from '../../../common/descriptor_types';
import { ILayer } from '../layer';
export type ImmutableSourceProperty = {
label: string;
value: string;
};
export type Attribution = {
url: string;
label: string;
};
export interface ISource {
createDefaultLayer(): ILayer;
destroy(): void;
@ -18,13 +29,16 @@ export interface ISource {
isQueryAware(): boolean;
isRefreshTimerAware(): Promise<boolean>;
isTimeAware(): Promise<boolean>;
getImmutableProperties(): Promise<ImmutableSourceProperty[]>;
getAttributions(): Promise<Attribution[]>;
}
export class AbstractSource implements ISource {
constructor(sourceDescriptor: AbstractSourceDescriptor, inspectorAdapters: object);
readonly _descriptor: AbstractSourceDescriptor;
constructor(sourceDescriptor: AbstractSourceDescriptor, inspectorAdapters?: object);
destroy(): void;
createDefaultLayer(): ILayer;
createDefaultLayer(options?: LayerDescriptor, mapColors?: string[]): ILayer;
getDisplayName(): Promise<string>;
getInspectorAdapters(): object;
isFieldAware(): boolean;
@ -33,4 +47,6 @@ export class AbstractSource implements ISource {
isQueryAware(): boolean;
isRefreshTimerAware(): Promise<boolean>;
isTimeAware(): Promise<boolean>;
getImmutableProperties(): Promise<ImmutableSourceProperty[]>;
getAttributions(): Promise<Attribution[]>;
}

View file

@ -5,12 +5,11 @@
*/
/* eslint-disable @typescript-eslint/consistent-type-definitions */
import { AbstractSourceDescriptor } from '../../../common/descriptor_types';
import { ISource } from './source';
type SourceRegistryEntry = {
ConstructorFunction: new (
sourceDescriptor: AbstractSourceDescriptor,
sourceDescriptor: any, // this is the source-descriptor that corresponds specifically to the particular ISource instance
inspectorAdapters: unknown
) => ISource;
type: string;

View file

@ -0,0 +1,7 @@
/*
* 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.
*/
export * from './tms_source';

View file

@ -4,7 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { AbstractSource, ISource } from './source';
import { AbstractSource, Attribution, ISource } from '../source';
export interface ITMSSource extends ISource {
getUrlTemplate(): Promise<string>;
@ -12,4 +12,5 @@ export interface ITMSSource extends ISource {
export class AbstractTMSSource extends AbstractSource implements ITMSSource {
getUrlTemplate(): Promise<string>;
getAttributions(): Promise<Attribution[]>;
}

View file

@ -4,7 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { AbstractSource } from './source';
import { AbstractSource } from '../source';
export class AbstractTMSSource extends AbstractSource {
async getUrlTemplate() {

View file

@ -0,0 +1,7 @@
/*
* 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.
*/
export * from './vector_source';

View file

@ -6,14 +6,14 @@
/* eslint-disable @typescript-eslint/consistent-type-definitions */
import { FeatureCollection } from 'geojson';
import { AbstractSource, ISource } from './source';
import { IField } from '../fields/field';
import { AbstractSource, ISource } from '../source';
import { IField } from '../../fields/field';
import {
ESSearchSourceResponseMeta,
MapExtent,
VectorSourceRequestMeta,
VectorSourceSyncMeta,
} from '../../../common/descriptor_types';
} from '../../../../common/descriptor_types';
export type GeoJsonFetchMeta = ESSearchSourceResponseMeta;

View file

@ -4,14 +4,14 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { VectorLayer } from '../vector_layer';
import { TooltipProperty } from '../tooltips/tooltip_property';
import { VectorStyle } from '../styles/vector/vector_style';
import { AbstractSource } from './source';
import { VectorLayer } from '../../vector_layer';
import { TooltipProperty } from '../../tooltips/tooltip_property';
import { VectorStyle } from '../../styles/vector/vector_style';
import { AbstractSource } from './../source';
import * as topojson from 'topojson-client';
import _ from 'lodash';
import { i18n } from '@kbn/i18n';
import { VECTOR_SHAPE_TYPES } from './vector_feature_types';
import { VECTOR_SHAPE_TYPES } from './../vector_feature_types';
export class AbstractVectorSource extends AbstractSource {
static async getGeoJson({ format, featureCollectionPath, fetchUrl }) {

View file

@ -1,11 +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 { AbstractTMSSource } from './tms_source';
import { XYZTMSSourceDescriptor } from '../../../common/descriptor_types';
export class XYZTMSSource extends AbstractTMSSource {
constructor(sourceDescriptor: XYZTMSSourceDescriptor, inspectorAdapters: unknown);
}

View file

@ -1,187 +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 React, { Fragment } from 'react';
import { EuiFieldText, EuiFormRow } from '@elastic/eui';
import { AbstractTMSSource } from './tms_source';
import { TileLayer } from '../tile_layer';
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 createDescriptor({ urlTemplate, attributionText, attributionUrl }) {
return {
type: XYZTMSSource.type,
urlTemplate,
attributionText,
attributionUrl,
};
}
async getImmutableProperties() {
return [
{ label: getDataSourceLabel(), value: sourceTitle },
{ label: getUrlLabel(), value: this._descriptor.urlTemplate },
];
}
_createDefaultLayerDescriptor(options) {
return TileLayer.createDescriptor({
sourceDescriptor: this._descriptor,
...options,
});
}
createDefaultLayer(options) {
return new TileLayer({
layerDescriptor: this._createDefaultLayerDescriptor(options),
source: this,
});
}
async getDisplayName() {
return this._descriptor.urlTemplate;
}
getAttributions() {
const { attributionText, attributionUrl } = this._descriptor;
const attributionComplete = !!attributionText && !!attributionUrl;
return attributionComplete
? [
{
url: attributionUrl,
label: attributionText,
},
]
: [];
}
getUrlTemplate() {
return this._descriptor.urlTemplate;
}
}
class XYZTMSEditor extends React.Component {
state = {
tmsInput: '',
tmsCanPreview: false,
attributionText: '',
attributionUrl: '',
};
_sourceConfigChange = _.debounce(updatedSourceConfig => {
if (this.state.tmsCanPreview) {
this.props.onSourceConfigChange(updatedSourceConfig);
}
}, 2000);
_handleTMSInputChange(e) {
const url = e.target.value;
const canPreview =
url.indexOf('{x}') >= 0 && url.indexOf('{y}') >= 0 && url.indexOf('{z}') >= 0;
this.setState(
{
tmsInput: url,
tmsCanPreview: canPreview,
},
() => this._sourceConfigChange({ urlTemplate: url })
);
}
_handleTMSAttributionChange(attributionUpdate) {
this.setState(attributionUpdate, () => {
const { attributionText, attributionUrl, tmsInput } = this.state;
if (tmsInput && attributionText && attributionUrl) {
this._sourceConfigChange({
urlTemplate: tmsInput,
attributionText,
attributionUrl,
});
}
});
}
render() {
const { attributionText, attributionUrl } = this.state;
return (
<Fragment>
<EuiFormRow label="Url">
<EuiFieldText
placeholder={'https://a.tile.openstreetmap.org/{z}/{x}/{y}.png'}
onChange={e => this._handleTMSInputChange(e)}
/>
</EuiFormRow>
<EuiFormRow
label="Attribution text"
isInvalid={attributionUrl !== '' && attributionText === ''}
error={[
i18n.translate('xpack.maps.xyztmssource.attributionText', {
defaultMessage: 'Attribution url must have accompanying text',
}),
]}
>
<EuiFieldText
placeholder={'© OpenStreetMap contributors'}
onChange={({ target }) =>
this._handleTMSAttributionChange({ attributionText: target.value })
}
/>
</EuiFormRow>
<EuiFormRow
label="Attribution link"
isInvalid={attributionText !== '' && attributionUrl === ''}
error={[
i18n.translate('xpack.maps.xyztmssource.attributionLink', {
defaultMessage: 'Attribution text must have an accompanying link',
}),
]}
>
<EuiFieldText
placeholder={'https://www.openstreetmap.org/copyright'}
onChange={({ target }) =>
this._handleTMSAttributionChange({ attributionUrl: target.value })
}
/>
</EuiFormRow>
</Fragment>
);
}
}
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

@ -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;
* you may not use this file except in compliance with the Elastic License.
*/
export * from './xyz_tms_source';
export * from './layer_wizard';

View file

@ -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;
* you may not use this file except in compliance with the Elastic License.
*/
import { i18n } from '@kbn/i18n';
import React from 'react';
import { XYZTMSEditor, XYZTMSSourceConfig } from './xyz_tms_editor';
import { XYZTMSSource, sourceTitle } from './xyz_tms_source';
import { LayerWizard, RenderWizardArguments } from '../../layer_wizard_registry';
export const tmsLayerWizardConfig: LayerWizard = {
description: i18n.translate('xpack.maps.source.ems_xyzDescription', {
defaultMessage: 'Tile map service configured in interface',
}),
icon: 'grid',
renderWizard: ({ onPreviewSource }: RenderWizardArguments) => {
const onSourceConfigChange = (sourceConfig: XYZTMSSourceConfig) => {
const sourceDescriptor = XYZTMSSource.createDescriptor(sourceConfig);
const source = new XYZTMSSource(sourceDescriptor);
onPreviewSource(source);
};
return <XYZTMSEditor onSourceConfigChange={onSourceConfigChange} />;
},
title: sourceTitle,
};

View file

@ -0,0 +1,122 @@
/*
* 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 React, { Fragment, Component, ChangeEvent } from 'react';
import _ from 'lodash';
import { EuiFormRow, EuiFieldText } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { AttributionDescriptor } from '../../../../common/descriptor_types';
export type XYZTMSSourceConfig = AttributionDescriptor & {
urlTemplate: string;
};
export interface Props {
onSourceConfigChange: (sourceConfig: XYZTMSSourceConfig) => void;
}
interface State {
tmsInput: string;
tmsCanPreview: boolean;
attributionText: string;
attributionUrl: string;
}
export class XYZTMSEditor extends Component<Props, State> {
state = {
tmsInput: '',
tmsCanPreview: false,
attributionText: '',
attributionUrl: '',
};
_sourceConfigChange = _.debounce((updatedSourceConfig: XYZTMSSourceConfig) => {
if (this.state.tmsCanPreview) {
this.props.onSourceConfigChange(updatedSourceConfig);
}
}, 2000);
_handleTMSInputChange(e: ChangeEvent<HTMLInputElement>) {
const url = e.target.value;
const canPreview =
url.indexOf('{x}') >= 0 && url.indexOf('{y}') >= 0 && url.indexOf('{z}') >= 0;
this.setState(
{
tmsInput: url,
tmsCanPreview: canPreview,
},
() => this._sourceConfigChange({ urlTemplate: url })
);
}
_handleTMSAttributionChange(attributionUpdate: AttributionDescriptor) {
this.setState(
{
attributionUrl: attributionUpdate.attributionUrl || '',
attributionText: attributionUpdate.attributionText || '',
},
() => {
const { attributionText, attributionUrl, tmsInput } = this.state;
if (tmsInput && attributionText && attributionUrl) {
this._sourceConfigChange({
urlTemplate: tmsInput,
attributionText,
attributionUrl,
});
}
}
);
}
render() {
const { attributionText, attributionUrl } = this.state;
return (
<Fragment>
<EuiFormRow label="Url">
<EuiFieldText
placeholder={'https://a.tile.openstreetmap.org/{z}/{x}/{y}.png'}
onChange={e => this._handleTMSInputChange(e)}
/>
</EuiFormRow>
<EuiFormRow
label="Attribution text"
isInvalid={attributionUrl !== '' && attributionText === ''}
error={[
i18n.translate('xpack.maps.xyztmssource.attributionText', {
defaultMessage: 'Attribution url must have accompanying text',
}),
]}
>
<EuiFieldText
placeholder={'© OpenStreetMap contributors'}
onChange={({ target }: ChangeEvent<HTMLInputElement>) =>
this._handleTMSAttributionChange({ attributionText: target.value })
}
/>
</EuiFormRow>
<EuiFormRow
label="Attribution link"
isInvalid={attributionText !== '' && attributionUrl === ''}
error={[
i18n.translate('xpack.maps.xyztmssource.attributionLink', {
defaultMessage: 'Attribution text must have an accompanying link',
}),
]}
>
<EuiFieldText
placeholder={'https://www.openstreetmap.org/copyright'}
onChange={({ target }: ChangeEvent<HTMLInputElement>) =>
this._handleTMSAttributionChange({ attributionUrl: target.value })
}
/>
</EuiFormRow>
</Fragment>
);
}
}

View file

@ -5,10 +5,10 @@
*/
import { XYZTMSSource } from './xyz_tms_source';
import { ILayer } from '../layer';
import { TileLayer } from '../tile_layer';
import { EMS_XYZ } from '../../../common/constants';
import { XYZTMSSourceDescriptor } from '../../../common/descriptor_types';
import { ILayer } from '../../layer';
import { TileLayer } from '../../tile_layer';
import { EMS_XYZ } from '../../../../common/constants';
import { XYZTMSSourceDescriptor } from '../../../../common/descriptor_types';
const descriptor: XYZTMSSourceDescriptor = {
type: EMS_XYZ,
@ -17,13 +17,13 @@ const descriptor: XYZTMSSourceDescriptor = {
};
describe('xyz Tilemap Source', () => {
it('should create a tile-layer', () => {
const source = new XYZTMSSource(descriptor, null);
const source = new XYZTMSSource(descriptor);
const layer: ILayer = source.createDefaultLayer();
expect(layer instanceof TileLayer).toEqual(true);
});
it('should echo url template for url template', async () => {
const source = new XYZTMSSource(descriptor, null);
const source = new XYZTMSSource(descriptor);
const template = await source.getUrlTemplate();
expect(template).toEqual(descriptor.urlTemplate);
});

View file

@ -0,0 +1,87 @@
/*
* 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 { i18n } from '@kbn/i18n';
import { TileLayer } from '../../tile_layer';
import { getDataSourceLabel, getUrlLabel } from '../../../../common/i18n_getters';
import { EMS_XYZ } from '../../../../common/constants';
import { registerSource } from '../source_registry';
import { AbstractTMSSource } from '../tms_source';
import { LayerDescriptor, XYZTMSSourceDescriptor } from '../../../../common/descriptor_types';
import { Attribution, ImmutableSourceProperty } from '../source';
import { XYZTMSSourceConfig } from './xyz_tms_editor';
export const sourceTitle = i18n.translate('xpack.maps.source.ems_xyzTitle', {
defaultMessage: 'Tile Map Service',
});
export class XYZTMSSource extends AbstractTMSSource {
static type = EMS_XYZ;
readonly _descriptor: XYZTMSSourceDescriptor;
static createDescriptor({
urlTemplate,
attributionText,
attributionUrl,
}: XYZTMSSourceConfig): XYZTMSSourceDescriptor {
return {
type: XYZTMSSource.type,
urlTemplate,
attributionText,
attributionUrl,
};
}
constructor(sourceDescriptor: XYZTMSSourceDescriptor) {
super(sourceDescriptor);
this._descriptor = sourceDescriptor;
}
async getImmutableProperties(): Promise<ImmutableSourceProperty[]> {
return [
{ label: getDataSourceLabel(), value: sourceTitle },
{ label: getUrlLabel(), value: this._descriptor.urlTemplate },
];
}
createDefaultLayer(options?: LayerDescriptor): TileLayer {
const layerDescriptor: LayerDescriptor = TileLayer.createDescriptor({
sourceDescriptor: this._descriptor,
...options,
});
return new TileLayer({
layerDescriptor,
source: this,
});
}
async getDisplayName(): Promise<string> {
return this._descriptor.urlTemplate;
}
async getAttributions(): Promise<Attribution[]> {
const { attributionText, attributionUrl } = this._descriptor;
const attributionComplete = !!attributionText && !!attributionUrl;
return attributionComplete
? [
{
url: attributionUrl as string,
label: attributionText as string,
},
]
: [];
}
async getUrlTemplate(): Promise<string> {
return this._descriptor.urlTemplate;
}
}
registerSource({
ConstructorFunction: XYZTMSSource,
type: EMS_XYZ,
});

View file

@ -11,8 +11,8 @@ import { SOURCE_DATA_ID_ORIGIN, LAYER_TYPE } from '../../common/constants';
export class TileLayer extends AbstractLayer {
static type = LAYER_TYPE.TILE;
static createDescriptor(options) {
const tileLayerDescriptor = super.createDescriptor(options);
static createDescriptor(options, mapColors) {
const tileLayerDescriptor = super.createDescriptor(options, mapColors);
tileLayerDescriptor.type = TileLayer.type;
tileLayerDescriptor.alpha = _.get(options, 'alpha', 1);
return tileLayerDescriptor;

View file

@ -17,7 +17,7 @@ const sourceDescriptor: XYZTMSSourceDescriptor = {
};
class MockTileSource extends AbstractTMSSource implements ITMSSource {
private readonly _descriptor: XYZTMSSourceDescriptor;
readonly _descriptor: XYZTMSSourceDescriptor;
constructor(descriptor: XYZTMSSourceDescriptor) {
super(descriptor, {});
this._descriptor = descriptor;

View file

@ -32,8 +32,8 @@ export interface IVectorLayer extends ILayer {
export class VectorLayer extends AbstractLayer implements IVectorLayer {
static createDescriptor(
options: VectorLayerArguments,
mapColors: string[]
options: Partial<VectorLayerDescriptor>,
mapColors?: string[]
): VectorLayerDescriptor;
protected readonly _source: IVectorSource;