diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/all_sources.js b/x-pack/legacy/plugins/maps/public/layers/sources/all_sources.js index 6a518609dd77f..3971a50f17d70 100644 --- a/x-pack/legacy/plugins/maps/public/layers/sources/all_sources.js +++ b/x-pack/legacy/plugins/maps/public/layers/sources/all_sources.js @@ -8,6 +8,7 @@ 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 { VectorTileSource } from './vector_tile_source'; import { EMSTMSSource } from './ems_tms_source'; import { WMSSource } from './wms_source'; import { KibanaTilemapSource } from './kibana_tilemap_source'; @@ -22,6 +23,7 @@ export const ALL_SOURCES = [ ESPewPewSource, EMSFileSource, EMSTMSSource, + VectorTileSource, KibanaRegionmapSource, KibanaTilemapSource, XYZTMSSource, diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/vector_tile_source.js b/x-pack/legacy/plugins/maps/public/layers/sources/vector_tile_source.js new file mode 100644 index 0000000000000..35ddb5f945bdd --- /dev/null +++ b/x-pack/legacy/plugins/maps/public/layers/sources/vector_tile_source.js @@ -0,0 +1,209 @@ +/* + * 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 _ from 'lodash'; +import crypto from 'crypto'; +import { AbstractSource } from './source'; +import { VectorTileLayer } from '../vector_tile_layer'; + +import { i18n } from '@kbn/i18n'; +import { getDataSourceLabel, getUrlLabel } from '../../../common/i18n_getters'; + +// Unlike raster tiles and EMS vector tiles, custom vector tiles can have multiple sources +// so we do not implement the getUrlTemplate function required by AbstractTMSSource. +export class VectorTileSource extends AbstractSource { + + static type = 'VECTOR_TILE'; + static title = i18n.translate('xpack.maps.source.vectorTileTitle', { + defaultMessage: 'Vector Tile Source' + }); + static description = i18n.translate('xpack.maps.source.vectorTileDescription', { + defaultMessage: 'Vector tile service configured in interface' + }); + static icon = 'grid'; + + static createDescriptor(sourceConfig) { + return { + type: VectorTileSource.type, + styleUrl: sourceConfig.styleUrl + }; + } + + static renderEditor({ onPreviewSource, inspectorAdapters }) { + const onSourceConfigChange = (sourceConfig) => { + const descriptor = VectorTileSource.createDescriptor(sourceConfig); + const source = new VectorTileSource(descriptor, inspectorAdapters); + onPreviewSource(source); + }; + + return ; + } + + async getImmutableProperties() { + return [ + { + label: getDataSourceLabel(), + value: VectorTileSource.title + }, + { + label: getUrlLabel(), + value: this._descriptor.styleUrl + } + ]; + } + + async _getVectorStyleJson() { + const resp = await fetch(this._descriptor.styleUrl); + if(!resp.ok) { + throw new Error(`Unable to access ${this._descriptor.styleUrl}`); + } + const style = await resp.json(); + return style; + } + + _createDefaultLayerDescriptor(options) { + return VectorTileLayer.createDescriptor({ + sourceDescriptor: this._descriptor, + ...options + }); + } + + createDefaultLayer(options) { + return new VectorTileLayer({ + layerDescriptor: this._createDefaultLayerDescriptor(options), + source: this + }); + } + + async getDisplayName() { + const styleJson = await this._getVectorStyleJson(); + return _.get(styleJson, 'name', this._descriptor.styleUrl); + } + + async getAttributions() { + // TODO Can mapbox-gl automatically retrieve this from style sources? + return []; + } + + getSpriteNamespacePrefix() { + return null; + } + + async getVectorStyleSheetAndSpriteMeta() { + const vectorStyleSheet = await this._getVectorStyleJson(); + return { + vectorStyleSheet + }; + } + + _getTileLayerId() { + return crypto.createHash('md5').update(this._descriptor.styleUrl).digest('hex'); + } +} + +class VectorTileSourceEditor extends React.Component { + + state = { + tmsInput: '', + tmsCanPreview: false, + attributionText: '', + attributionUrl: '', + } + + _sourceConfigChange = _.debounce(updatedSourceConfig => { + if (this.state.tmsCanPreview) { + this.props.onSourceConfigChange(updatedSourceConfig); + } + }, 2000); + + _handleStyleInputChange(e) { + const url = e.target.value; + + const canPreview = true; + this.setState({ + tmsInput: url, + tmsCanPreview: canPreview + }, () => this._sourceConfigChange({ styleUrl: url })); + } + + _handleAttributionChange(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 ( + + + this._handleStyleInputChange(e)} + /> + + + + this._handleAttributionChange({ attributionText: target.value }) + } + /> + + + + this._handleAttributionChange({ attributionUrl: target.value }) + } + /> + + + ); + } +} diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/xyz_tms_source.js b/x-pack/legacy/plugins/maps/public/layers/sources/xyz_tms_source.js index 4a370e7236933..166ec9c6a18bc 100644 --- a/x-pack/legacy/plugins/maps/public/layers/sources/xyz_tms_source.js +++ b/x-pack/legacy/plugins/maps/public/layers/sources/xyz_tms_source.js @@ -20,10 +20,10 @@ export class XYZTMSSource extends AbstractTMSSource { static type = 'EMS_XYZ'; static title = i18n.translate('xpack.maps.source.ems_xyzTitle', { - defaultMessage: 'Tile Map Service' + defaultMessage: 'Raster Tile Map Service' }); static description = i18n.translate('xpack.maps.source.ems_xyzDescription', { - defaultMessage: 'Tile map service configured in interface' + defaultMessage: 'Raster tile map service configured in interface' }); static icon = 'grid'; diff --git a/x-pack/legacy/plugins/maps/public/layers/vector_tile_layer.js b/x-pack/legacy/plugins/maps/public/layers/vector_tile_layer.js index a7f0346b09ef5..5d03dec56c68a 100644 --- a/x-pack/legacy/plugins/maps/public/layers/vector_tile_layer.js +++ b/x-pack/legacy/plugins/maps/public/layers/vector_tile_layer.js @@ -106,8 +106,11 @@ export class VectorTileLayer extends TileLayer { } _makeNamespacedImageId(imageId) { - const prefix = this._source.getSpriteNamespacePrefix() + '/'; - return prefix + imageId; + const prefix = this._source.getSpriteNamespacePrefix(); + if (prefix) { + return `${prefix}/${imageId}`; + } + return imageId; } syncLayerWithMB(mbMap) { @@ -139,17 +142,16 @@ export class VectorTileLayer extends TileLayer { //sync spritesheet const spriteMeta = this._getSpriteMeta(); - if (!spriteMeta) { - return; - } - const newJson = {}; - for (const imageId in spriteMeta.json) { - if (spriteMeta.json.hasOwnProperty(imageId)) { - const namespacedImageId = this._makeNamespacedImageId(imageId); - newJson[namespacedImageId] = spriteMeta.json[imageId]; + if (spriteMeta) { + const newJson = {}; + for (const imageId in spriteMeta.json) { + if (spriteMeta.json.hasOwnProperty(imageId)) { + const namespacedImageId = this._makeNamespacedImageId(imageId); + newJson[namespacedImageId] = spriteMeta.json[imageId]; + } } + addSpritesheetToMap(newJson, spriteMeta.png, mbMap); } - addSpritesheetToMap(newJson, spriteMeta.png, mbMap); //sync layers vectorStyle.layers.forEach(layer => {