mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
[Maps] convert EMS TMS source to typescript (#116508)
* [Maps] convert EMS TMS source to typescript * eslint Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
0a3cd8ebab
commit
7c73e227eb
18 changed files with 110 additions and 159 deletions
|
@ -351,7 +351,7 @@ When `includeElasticMapsService` is turned off, only tile layer configured by <<
|
|||
| Specifies the URL of a self hosted <<elastic-maps-server,{hosted-ems}>>
|
||||
|
||||
| [[tilemap-settings]] `map.tilemap.options.attribution:` {ess-icon}
|
||||
| The map attribution string.
|
||||
| The map attribution string. Provide attributions in markdown and use '|' to delimit attributions, for example: `"[attribution 1](https://www.attribution1)|[attribution 2](https://www.attribution2)"`.
|
||||
*Default: `"© [Elastic Maps Service](https://www.elastic.co/elastic-maps-service)"`*
|
||||
|
||||
| [[tilemap-max-zoom]] `map.tilemap.options.maxZoom:` {ess-icon}
|
||||
|
|
|
@ -13,7 +13,6 @@ import { getEMSSettings } from '../../kibana_services';
|
|||
import { KibanaTilemapSource } from '../sources/kibana_tilemap_source';
|
||||
import { TileLayer } from './tile_layer/tile_layer';
|
||||
import { VectorTileLayer } from './vector_tile_layer/vector_tile_layer';
|
||||
// @ts-expect-error
|
||||
import { EMSTMSSource } from '../sources/ems_tms_source';
|
||||
|
||||
export function createBasemapLayerDescriptor(): LayerDescriptor | null {
|
||||
|
|
|
@ -8,7 +8,8 @@
|
|||
import { ITileLayerArguments, TileLayer } from './tile_layer';
|
||||
import { SOURCE_TYPES } from '../../../../common/constants';
|
||||
import { XYZTMSSourceDescriptor } from '../../../../common/descriptor_types';
|
||||
import { ITMSSource, AbstractTMSSource } from '../../sources/tms_source';
|
||||
import { AbstractSource } from '../../sources/source';
|
||||
import { ITMSSource } from '../../sources/tms_source';
|
||||
import { ILayer } from '../layer';
|
||||
|
||||
const sourceDescriptor: XYZTMSSourceDescriptor = {
|
||||
|
@ -17,7 +18,7 @@ const sourceDescriptor: XYZTMSSourceDescriptor = {
|
|||
id: 'foobar',
|
||||
};
|
||||
|
||||
class MockTileSource extends AbstractTMSSource implements ITMSSource {
|
||||
class MockTileSource extends AbstractSource implements ITMSSource {
|
||||
readonly _descriptor: XYZTMSSourceDescriptor;
|
||||
constructor(descriptor: XYZTMSSourceDescriptor) {
|
||||
super(descriptor, {});
|
||||
|
|
|
@ -8,7 +8,8 @@
|
|||
import { ITileLayerArguments } from '../tile_layer/tile_layer';
|
||||
import { SOURCE_TYPES } from '../../../../common/constants';
|
||||
import { DataFilters, XYZTMSSourceDescriptor } from '../../../../common/descriptor_types';
|
||||
import { ITMSSource, AbstractTMSSource } from '../../sources/tms_source';
|
||||
import { AbstractSource } from '../../sources/source';
|
||||
import { ITMSSource } from '../../sources/tms_source';
|
||||
import { ILayer } from '../layer';
|
||||
import { VectorTileLayer } from './vector_tile_layer';
|
||||
import { DataRequestContext } from '../../../actions';
|
||||
|
@ -19,7 +20,7 @@ const sourceDescriptor: XYZTMSSourceDescriptor = {
|
|||
id: 'mockSourceId',
|
||||
};
|
||||
|
||||
class MockTileSource extends AbstractTMSSource implements ITMSSource {
|
||||
class MockTileSource extends AbstractSource implements ITMSSource {
|
||||
readonly _descriptor: XYZTMSSourceDescriptor;
|
||||
constructor(descriptor: XYZTMSSourceDescriptor) {
|
||||
super(descriptor, {});
|
||||
|
|
|
@ -21,7 +21,7 @@ export class CreateSourceEditor extends Component<Props, State> {
|
|||
state: State = {};
|
||||
|
||||
componentDidMount() {
|
||||
this._onTileSelect({ id: null, isAutoSelect: true });
|
||||
this._onTileSelect({ isAutoSelect: true });
|
||||
}
|
||||
|
||||
_onTileSelect = (config: EmsTmsSourceConfig) => {
|
||||
|
|
|
@ -1,70 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
jest.mock('../../../kibana_services', () => {
|
||||
return {
|
||||
getEmsTileLayerId: () => {
|
||||
return {
|
||||
bright: 'road_map',
|
||||
desaturated: 'road_map_desaturated',
|
||||
dark: 'dark_map',
|
||||
};
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
jest.mock('../../../util', () => {
|
||||
return {
|
||||
getEmsTmsServices: () => {
|
||||
class MockTMSService {
|
||||
constructor(config) {
|
||||
this._config = config;
|
||||
}
|
||||
getMarkdownAttribution() {
|
||||
return this._config.attributionMarkdown;
|
||||
}
|
||||
getId() {
|
||||
return this._config.id;
|
||||
}
|
||||
}
|
||||
|
||||
return [
|
||||
new MockTMSService({
|
||||
id: 'road_map',
|
||||
attributionMarkdown: '[foobar](http://foobar.org) | [foobaz](http://foobaz.org)',
|
||||
}),
|
||||
new MockTMSService({
|
||||
id: 'satellite',
|
||||
attributionMarkdown: '[satellite](http://satellite.org)',
|
||||
}),
|
||||
];
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
import { EMSTMSSource } from './ems_tms_source';
|
||||
|
||||
describe('EMSTMSSource', () => {
|
||||
it('should get attribution from markdown (tiles v2 legacy format)', async () => {
|
||||
const emsTmsSource = new EMSTMSSource({
|
||||
id: 'road_map',
|
||||
});
|
||||
|
||||
const attributionProvider = emsTmsSource.getAttributionProvider();
|
||||
const attributions = await attributionProvider();
|
||||
expect(attributions).toEqual([
|
||||
{
|
||||
label: 'foobar',
|
||||
url: 'http://foobar.org',
|
||||
},
|
||||
{
|
||||
label: 'foobaz',
|
||||
url: 'http://foobaz.org',
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
|
@ -6,18 +6,21 @@
|
|||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { AbstractTMSSource } from '../tms_source';
|
||||
import { Adapters } from 'src/plugins/inspector/public';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { AbstractSource, SourceEditorArgs } from '../source';
|
||||
import { ITMSSource } from '../tms_source';
|
||||
import { getEmsTmsServices } from '../../../util';
|
||||
import { UpdateSourceEditor } from './update_source_editor';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { getDataSourceLabel } from '../../../../common/i18n_getters';
|
||||
import { SOURCE_TYPES } from '../../../../common/constants';
|
||||
import { EMSTMSSourceDescriptor } from '../../../../common/descriptor_types';
|
||||
import { getEmsTileLayerId, getIsDarkMode, getEMSSettings } from '../../../kibana_services';
|
||||
import { registerSource } from '../source_registry';
|
||||
import { getEmsUnavailableMessage } from '../../../components/ems_unavailable_message';
|
||||
import { LICENSED_FEATURES } from '../../../licensed_features';
|
||||
|
||||
function getErrorInfo(emsTileLayerId) {
|
||||
function getErrorInfo(emsTileLayerId: string) {
|
||||
return i18n.translate('xpack.maps.source.emsTile.unableToFindTileIdErrorMessage', {
|
||||
defaultMessage: `Unable to find EMS tile configuration for id: {id}. {info}`,
|
||||
values: { id: emsTileLayerId, info: getEmsUnavailableMessage() },
|
||||
|
@ -37,8 +40,8 @@ export function getSourceTitle() {
|
|||
}
|
||||
}
|
||||
|
||||
export class EMSTMSSource extends AbstractTMSSource {
|
||||
static createDescriptor(descriptor) {
|
||||
export class EMSTMSSource extends AbstractSource implements ITMSSource {
|
||||
static createDescriptor(descriptor: Partial<EMSTMSSourceDescriptor>): EMSTMSSourceDescriptor {
|
||||
return {
|
||||
type: SOURCE_TYPES.EMS_TMS,
|
||||
id: descriptor.id,
|
||||
|
@ -51,12 +54,15 @@ export class EMSTMSSource extends AbstractTMSSource {
|
|||
};
|
||||
}
|
||||
|
||||
constructor(descriptor, inspectorAdapters) {
|
||||
descriptor = EMSTMSSource.createDescriptor(descriptor);
|
||||
super(descriptor, inspectorAdapters);
|
||||
readonly _descriptor: EMSTMSSourceDescriptor;
|
||||
|
||||
constructor(descriptor: Partial<EMSTMSSourceDescriptor>, inspectorAdapters?: Adapters) {
|
||||
const emsTmsDescriptor = EMSTMSSource.createDescriptor(descriptor);
|
||||
super(emsTmsDescriptor, inspectorAdapters);
|
||||
this._descriptor = emsTmsDescriptor;
|
||||
}
|
||||
|
||||
renderSourceSettingsEditor({ onChange }) {
|
||||
renderSourceSettingsEditor({ onChange }: SourceEditorArgs) {
|
||||
return <UpdateSourceEditor onChange={onChange} config={this._descriptor} />;
|
||||
}
|
||||
|
||||
|
@ -100,12 +106,12 @@ export class EMSTMSSource extends AbstractTMSSource {
|
|||
} catch (e) {
|
||||
throw new Error(`${getErrorInfo(emsTileLayerId)} - ${e.message}`);
|
||||
}
|
||||
const tmsService = emsTMSServices.find((tmsService) => tmsService.getId() === emsTileLayerId);
|
||||
if (tmsService) {
|
||||
return tmsService;
|
||||
const tmsService = emsTMSServices.find((service) => service.getId() === emsTileLayerId);
|
||||
if (!tmsService) {
|
||||
throw new Error(getErrorInfo(emsTileLayerId));
|
||||
}
|
||||
|
||||
throw new Error(getErrorInfo(emsTileLayerId));
|
||||
return tmsService;
|
||||
}
|
||||
|
||||
async getDisplayName() {
|
||||
|
@ -120,15 +126,11 @@ export class EMSTMSSource extends AbstractTMSSource {
|
|||
getAttributionProvider() {
|
||||
return async () => {
|
||||
const emsTMSService = await this._getEMSTMSService();
|
||||
const markdown = emsTMSService.getMarkdownAttribution();
|
||||
if (!markdown) {
|
||||
return [];
|
||||
}
|
||||
return this.convertMarkdownLinkToObjectArr(markdown);
|
||||
return emsTMSService.getAttributions();
|
||||
};
|
||||
}
|
||||
|
||||
async getUrlTemplate() {
|
||||
async getUrlTemplate(): Promise<string> {
|
||||
const emsTMSService = await this._getEMSTMSService();
|
||||
return await emsTMSService.getUrlTemplate();
|
||||
}
|
||||
|
@ -137,13 +139,13 @@ export class EMSTMSSource extends AbstractTMSSource {
|
|||
return 'ems/' + this.getTileLayerId();
|
||||
}
|
||||
|
||||
async getVectorStyleSheetAndSpriteMeta(isRetina) {
|
||||
async getVectorStyleSheetAndSpriteMeta(isRetina: boolean) {
|
||||
const emsTMSService = await this._getEMSTMSService();
|
||||
const styleSheet = await emsTMSService.getVectorStyleSheet();
|
||||
const spriteMeta = await emsTMSService.getSpriteSheetMeta(isRetina);
|
||||
return {
|
||||
vectorStyleSheet: styleSheet,
|
||||
spriteMeta: spriteMeta,
|
||||
spriteMeta,
|
||||
};
|
||||
}
|
||||
|
|
@ -9,15 +9,13 @@ import React, { ChangeEvent, Component } from 'react';
|
|||
import { EuiSelect, EuiSelectOption, EuiFormRow } from '@elastic/eui';
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { EMSTMSSourceDescriptor } from '../../../../common/descriptor_types';
|
||||
import { getEmsTmsServices } from '../../../util';
|
||||
import { getEmsUnavailableMessage } from '../../../components/ems_unavailable_message';
|
||||
|
||||
const AUTO_SELECT = 'auto_select';
|
||||
|
||||
export interface EmsTmsSourceConfig {
|
||||
id: string | null;
|
||||
isAutoSelect: boolean;
|
||||
}
|
||||
export type EmsTmsSourceConfig = Pick<EMSTMSSourceDescriptor, 'id' | 'isAutoSelect'>;
|
||||
|
||||
interface Props {
|
||||
config?: EmsTmsSourceConfig;
|
||||
|
@ -72,7 +70,7 @@ export class TileServiceSelect extends Component<Props, State> {
|
|||
const value = e.target.value;
|
||||
const isAutoSelect = value === AUTO_SELECT;
|
||||
this.props.onTileSelect({
|
||||
id: isAutoSelect ? null : value,
|
||||
id: isAutoSelect ? undefined : value,
|
||||
isAutoSelect,
|
||||
});
|
||||
};
|
||||
|
|
|
@ -8,10 +8,16 @@
|
|||
import React, { Fragment } from 'react';
|
||||
import { EuiTitle, EuiPanel, EuiSpacer } from '@elastic/eui';
|
||||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
import { TileServiceSelect } from './tile_service_select';
|
||||
import { EmsTmsSourceConfig, TileServiceSelect } from './tile_service_select';
|
||||
import { OnSourceChangeArgs } from '../source';
|
||||
|
||||
export function UpdateSourceEditor({ onChange, config }) {
|
||||
const _onTileSelect = ({ id, isAutoSelect }) => {
|
||||
interface Props {
|
||||
onChange: (...args: OnSourceChangeArgs[]) => Promise<void>;
|
||||
config: EmsTmsSourceConfig;
|
||||
}
|
||||
|
||||
export function UpdateSourceEditor({ onChange, config }: Props) {
|
||||
const _onTileSelect = ({ id, isAutoSelect }: EmsTmsSourceConfig) => {
|
||||
onChange({ propName: 'id', value: id });
|
||||
onChange({ propName: 'isAutoSelect', value: isAutoSelect });
|
||||
};
|
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* 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 { extractAttributions } from './extract_attributions';
|
||||
|
||||
test('Should extract attributions from markdown', () => {
|
||||
const markdown =
|
||||
'[OpenStreetMap contributors](https://www.openstreetmap.org/copyright)|[OpenMapTiles](https://openmaptiles.org)|[Elastic Maps Service](https://www.elastic.co/elastic-maps-service)';
|
||||
const attributions = extractAttributions(markdown);
|
||||
expect(attributions).toEqual([
|
||||
{
|
||||
label: 'OpenStreetMap contributors',
|
||||
url: 'https://www.openstreetmap.org/copyright',
|
||||
},
|
||||
{
|
||||
label: 'OpenMapTiles',
|
||||
url: 'https://openmaptiles.org',
|
||||
},
|
||||
{
|
||||
label: 'Elastic Maps Service',
|
||||
url: 'https://www.elastic.co/elastic-maps-service',
|
||||
},
|
||||
]);
|
||||
});
|
|
@ -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 { Attribution } from '../../../../common/descriptor_types';
|
||||
|
||||
export function extractAttributions(markdown: string): Attribution[] {
|
||||
const attributions: Attribution[] = [];
|
||||
markdown.split('|').forEach((attribution: string) => {
|
||||
attribution = attribution.trim();
|
||||
// this assumes attribution is plain markdown link
|
||||
const extractLink = /\[(.*)\]\((.*)\)/;
|
||||
const result = extractLink.exec(attribution);
|
||||
if (result && result?.length >= 3 && result[1] && result[2]) {
|
||||
attributions.push({
|
||||
label: result[1],
|
||||
url: result[2],
|
||||
});
|
||||
}
|
||||
});
|
||||
return attributions;
|
||||
}
|
|
@ -5,19 +5,20 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { AbstractTMSSource } from '../tms_source';
|
||||
import { AbstractSource } from '../source';
|
||||
import { getKibanaTileMap } from '../../../util';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { getDataSourceLabel } from '../../../../common/i18n_getters';
|
||||
import _ from 'lodash';
|
||||
import { SOURCE_TYPES } from '../../../../common/constants';
|
||||
import { registerSource } from '../source_registry';
|
||||
import { extractAttributions } from './extract_attributions';
|
||||
|
||||
export const sourceTitle = i18n.translate('xpack.maps.source.kbnTMSTitle', {
|
||||
defaultMessage: 'Configured Tile Map Service',
|
||||
});
|
||||
|
||||
export class KibanaTilemapSource extends AbstractTMSSource {
|
||||
export class KibanaTilemapSource extends AbstractSource {
|
||||
static type = SOURCE_TYPES.KIBANA_TILEMAP;
|
||||
|
||||
static createDescriptor() {
|
||||
|
@ -57,8 +58,7 @@ export class KibanaTilemapSource extends AbstractTMSSource {
|
|||
return async () => {
|
||||
const tilemap = getKibanaTileMap();
|
||||
const markdown = _.get(tilemap, 'options.attribution', '');
|
||||
const objArr = this.convertMarkdownLinkToObjectArr(markdown);
|
||||
return objArr;
|
||||
return extractAttributions(markdown);
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -5,4 +5,8 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
export * from './tms_source';
|
||||
import { ISource } from '../source';
|
||||
|
||||
export interface ITMSSource extends ISource {
|
||||
getUrlTemplate(): Promise<string>;
|
||||
}
|
||||
|
|
|
@ -1,16 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { AbstractSource, ISource } from '../source';
|
||||
|
||||
export interface ITMSSource extends ISource {
|
||||
getUrlTemplate(): Promise<string>;
|
||||
}
|
||||
|
||||
export class AbstractTMSSource extends AbstractSource implements ITMSSource {
|
||||
getUrlTemplate(): Promise<string>;
|
||||
}
|
|
@ -1,27 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { AbstractSource } from '../source';
|
||||
|
||||
export class AbstractTMSSource extends AbstractSource {
|
||||
async getUrlTemplate() {
|
||||
throw new Error('Should implement TMSSource#getUrlTemplate');
|
||||
}
|
||||
|
||||
convertMarkdownLinkToObjectArr(markdown) {
|
||||
return markdown.split('|').map((attribution) => {
|
||||
attribution = attribution.trim();
|
||||
//this assumes attribution is plain markdown link
|
||||
const extractLink = /\[(.*)\]\((.*)\)/;
|
||||
const result = extractLink.exec(attribution);
|
||||
return {
|
||||
label: result ? result[1] : null,
|
||||
url: result ? result[2] : null,
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
|
@ -5,7 +5,7 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { AbstractTMSSource } from '../tms_source';
|
||||
import { AbstractSource } from '../source';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { getDataSourceLabel, getUrlLabel } from '../../../../common/i18n_getters';
|
||||
import { WmsClient } from './wms_client';
|
||||
|
@ -16,7 +16,7 @@ export const sourceTitle = i18n.translate('xpack.maps.source.wmsTitle', {
|
|||
defaultMessage: 'Web Map Service',
|
||||
});
|
||||
|
||||
export class WMSSource extends AbstractTMSSource {
|
||||
export class WMSSource extends AbstractSource {
|
||||
static type = SOURCE_TYPES.WMS;
|
||||
|
||||
static createDescriptor({ serviceUrl, layers, styles }) {
|
||||
|
|
|
@ -9,16 +9,16 @@ import { i18n } from '@kbn/i18n';
|
|||
import { getDataSourceLabel, getUrlLabel } from '../../../../common/i18n_getters';
|
||||
import { SOURCE_TYPES } from '../../../../common/constants';
|
||||
import { registerSource } from '../source_registry';
|
||||
import { AbstractTMSSource } from '../tms_source';
|
||||
import { ITMSSource } from '../tms_source';
|
||||
import { XYZTMSSourceDescriptor } from '../../../../common/descriptor_types';
|
||||
import { ImmutableSourceProperty } from '../source';
|
||||
import { AbstractSource, 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 {
|
||||
export class XYZTMSSource extends AbstractSource implements ITMSSource {
|
||||
static type = SOURCE_TYPES.EMS_XYZ;
|
||||
|
||||
readonly _descriptor: XYZTMSSourceDescriptor;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue