From 7d90bad9604aab74a6477f633b6de1f2d7094f8f Mon Sep 17 00:00:00 2001 From: Nathan Reese Date: Mon, 8 Nov 2021 18:39:14 -0700 Subject: [PATCH] [Maps] convert TileLayer and VectorTileLayer to TS (#117745) * [Maps] convert TileLayer and VectorTileLayer to TS * commit using @elastic.co Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../maps/public/classes/layers/layer.tsx | 2 +- .../classes/layers/tile_layer/tile_layer.d.ts | 21 --- .../{tile_layer.js => tile_layer.ts} | 48 ++++-- .../vector_tile_layer/vector_tile_layer.d.ts | 13 -- ...or_tile_layer.js => vector_tile_layer.tsx} | 160 ++++++++++++------ 5 files changed, 142 insertions(+), 102 deletions(-) delete mode 100644 x-pack/plugins/maps/public/classes/layers/tile_layer/tile_layer.d.ts rename x-pack/plugins/maps/public/classes/layers/tile_layer/{tile_layer.js => tile_layer.ts} (66%) delete mode 100644 x-pack/plugins/maps/public/classes/layers/vector_tile_layer/vector_tile_layer.d.ts rename x-pack/plugins/maps/public/classes/layers/vector_tile_layer/{vector_tile_layer.js => vector_tile_layer.tsx} (63%) diff --git a/x-pack/plugins/maps/public/classes/layers/layer.tsx b/x-pack/plugins/maps/public/classes/layers/layer.tsx index 051115a072608..21fee2e3dfdce 100644 --- a/x-pack/plugins/maps/public/classes/layers/layer.tsx +++ b/x-pack/plugins/maps/public/classes/layers/layer.tsx @@ -466,7 +466,7 @@ export class AbstractLayer implements ILayer { return null; } - isBasemap(): boolean { + isBasemap(order: number): boolean { return false; } } diff --git a/x-pack/plugins/maps/public/classes/layers/tile_layer/tile_layer.d.ts b/x-pack/plugins/maps/public/classes/layers/tile_layer/tile_layer.d.ts deleted file mode 100644 index e83eff53c57c8..0000000000000 --- a/x-pack/plugins/maps/public/classes/layers/tile_layer/tile_layer.d.ts +++ /dev/null @@ -1,21 +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 { AbstractLayer } from '../layer'; -import { ITMSSource } from '../../sources/tms_source'; -import { LayerDescriptor } from '../../../../common/descriptor_types'; - -interface ITileLayerArguments { - source: ITMSSource; - layerDescriptor: LayerDescriptor; -} - -export class TileLayer extends AbstractLayer { - static type: string; - - constructor(args: ITileLayerArguments); -} diff --git a/x-pack/plugins/maps/public/classes/layers/tile_layer/tile_layer.js b/x-pack/plugins/maps/public/classes/layers/tile_layer/tile_layer.ts similarity index 66% rename from x-pack/plugins/maps/public/classes/layers/tile_layer/tile_layer.js rename to x-pack/plugins/maps/public/classes/layers/tile_layer/tile_layer.ts index 6ff5377a06ed9..b8a9924606198 100644 --- a/x-pack/plugins/maps/public/classes/layers/tile_layer/tile_layer.js +++ b/x-pack/plugins/maps/public/classes/layers/tile_layer/tile_layer.ts @@ -5,25 +5,41 @@ * 2.0. */ -import { AbstractLayer } from '../layer'; +import type { Map as MbMap } from '@kbn/mapbox-gl'; import _ from 'lodash'; +import { AbstractLayer } from '../layer'; import { SOURCE_DATA_REQUEST_ID, LAYER_TYPE, LAYER_STYLE_TYPE } from '../../../../common/constants'; +import { LayerDescriptor } from '../../../../common/descriptor_types'; import { TileStyle } from '../../styles/tile/tile_style'; +import { ITMSSource } from '../../sources/tms_source'; +import { DataRequestContext } from '../../../actions'; + +export interface ITileLayerArguments { + source: ITMSSource; + layerDescriptor: LayerDescriptor; +} +// TODO - rename to RasterTileLayer export class TileLayer extends AbstractLayer { - static createDescriptor(options, mapColors) { - const tileLayerDescriptor = super.createDescriptor(options, mapColors); + static createDescriptor(options: Partial) { + const tileLayerDescriptor = super.createDescriptor(options); tileLayerDescriptor.type = LAYER_TYPE.TILE; tileLayerDescriptor.alpha = _.get(options, 'alpha', 1); tileLayerDescriptor.style = { type: LAYER_STYLE_TYPE.TILE }; return tileLayerDescriptor; } - constructor({ source, layerDescriptor }) { + private readonly _style: TileStyle; + + constructor({ source, layerDescriptor }: ITileLayerArguments) { super({ source, layerDescriptor }); this._style = new TileStyle(); } + getSource(): ITMSSource { + return super.getSource() as ITMSSource; + } + getStyleForEditing() { return this._style; } @@ -36,10 +52,10 @@ export class TileLayer extends AbstractLayer { return this._style; } - async syncData({ startLoading, stopLoading, onLoadError, dataFilters }) { + async syncData({ startLoading, stopLoading, onLoadError, dataFilters }: DataRequestContext) { const sourceDataRequest = this.getSourceDataRequest(); if (sourceDataRequest) { - //data is immmutable + // data is immmutable return; } const requestToken = Symbol(`layer-source-refresh:${this.getId()} - source`); @@ -60,28 +76,28 @@ export class TileLayer extends AbstractLayer { return [this._getMbLayerId()]; } - ownsMbLayerId(mbLayerId) { + ownsMbLayerId(mbLayerId: string) { return this._getMbLayerId() === mbLayerId; } - ownsMbSourceId(mbSourceId) { + ownsMbSourceId(mbSourceId: string) { return this.getId() === mbSourceId; } - syncLayerWithMB(mbMap) { + syncLayerWithMB(mbMap: MbMap) { const source = mbMap.getSource(this.getId()); const mbLayerId = this._getMbLayerId(); if (!source) { const sourceDataRequest = this.getSourceDataRequest(); if (!sourceDataRequest) { - //this is possible if the layer was invisible at startup. - //the actions will not perform any data=syncing as an optimization when a layer is invisible - //when turning the layer back into visible, it's possible the url has not been resovled yet. + // this is possible if the layer was invisible at startup. + // the actions will not perform any data=syncing as an optimization when a layer is invisible + // when turning the layer back into visible, it's possible the url has not been resovled yet. return; } - const tmsSourceData = sourceDataRequest.getData(); + const tmsSourceData = sourceDataRequest.getData() as { url?: string }; if (!tmsSourceData || !tmsSourceData.url) { return; } @@ -106,9 +122,9 @@ export class TileLayer extends AbstractLayer { this._setTileLayerProperties(mbMap, mbLayerId); } - _setTileLayerProperties(mbMap, mbLayerId) { + _setTileLayerProperties(mbMap: MbMap, mbLayerId: string) { this.syncVisibilityWithMb(mbMap, mbLayerId); - mbMap.setLayerZoomRange(mbLayerId, this._descriptor.minZoom, this._descriptor.maxZoom); + mbMap.setLayerZoomRange(mbLayerId, this.getMinZoom(), this.getMaxZoom()); mbMap.setPaintProperty(mbLayerId, 'raster-opacity', this.getAlpha()); } @@ -116,7 +132,7 @@ export class TileLayer extends AbstractLayer { return 'grid'; } - isBasemap(order) { + isBasemap(order: number) { return order === 0; } } diff --git a/x-pack/plugins/maps/public/classes/layers/vector_tile_layer/vector_tile_layer.d.ts b/x-pack/plugins/maps/public/classes/layers/vector_tile_layer/vector_tile_layer.d.ts deleted file mode 100644 index a1f49f0e1d0b3..0000000000000 --- a/x-pack/plugins/maps/public/classes/layers/vector_tile_layer/vector_tile_layer.d.ts +++ /dev/null @@ -1,13 +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 { ITileLayerArguments, TileLayer } from '../tile_layer/tile_layer'; - -export class VectorTileLayer extends TileLayer { - static type: string; - constructor(args: ITileLayerArguments); -} diff --git a/x-pack/plugins/maps/public/classes/layers/vector_tile_layer/vector_tile_layer.js b/x-pack/plugins/maps/public/classes/layers/vector_tile_layer/vector_tile_layer.tsx similarity index 63% rename from x-pack/plugins/maps/public/classes/layers/vector_tile_layer/vector_tile_layer.js rename to x-pack/plugins/maps/public/classes/layers/vector_tile_layer/vector_tile_layer.tsx index 5096e5e29bf23..2b36ecc160954 100644 --- a/x-pack/plugins/maps/public/classes/layers/vector_tile_layer/vector_tile_layer.js +++ b/x-pack/plugins/maps/public/classes/layers/vector_tile_layer/vector_tile_layer.tsx @@ -5,27 +5,52 @@ * 2.0. */ -import { TileLayer } from '../tile_layer/tile_layer'; +import type { Map as MbMap, Layer as MbLayer, Style as MbStyle } from '@kbn/mapbox-gl'; import _ from 'lodash'; +import { TileLayer } from '../tile_layer/tile_layer'; import { SOURCE_DATA_REQUEST_ID, LAYER_TYPE, LAYER_STYLE_TYPE } from '../../../../common/constants'; +import { LayerDescriptor } from '../../../../common/descriptor_types'; +import { DataRequest } from '../../util/data_request'; import { isRetina } from '../../../util'; import { addSpriteSheetToMapFromImageData, loadSpriteSheetImageData, + // @ts-expect-error } from '../../../connected_components/mb_map/utils'; +import { DataRequestContext } from '../../../actions'; +import { EMSTMSSource } from '../../sources/ems_tms_source'; + +interface SourceRequestMeta { + tileLayerId: string; +} -const MB_STYLE_TYPE_TO_OPACITY = { - fill: ['fill-opacity'], - line: ['line-opacity'], - circle: ['circle-opacity'], - background: ['background-opacity'], - symbol: ['icon-opacity', 'text-opacity'], -}; +// TODO remove once ems_client exports EmsSpriteSheet and EmsSprite type +interface EmsSprite { + height: number; + pixelRatio: number; + width: number; + x: number; + y: number; +} + +interface EmsSpriteSheet { + [spriteName: string]: EmsSprite; +} +interface SourceRequestData { + spriteSheetImageData?: ImageData; + vectorStyleSheet?: MbStyle; + spriteMeta?: { + png: string; + json: EmsSpriteSheet; + }; +} + +// TODO - rename to EmsVectorTileLayer export class VectorTileLayer extends TileLayer { static type = LAYER_TYPE.VECTOR_TILE; - static createDescriptor(options) { + static createDescriptor(options: Partial) { const tileLayerDescriptor = super.createDescriptor(options); tileLayerDescriptor.type = VectorTileLayer.type; tileLayerDescriptor.alpha = _.get(options, 'alpha', 1); @@ -33,11 +58,21 @@ export class VectorTileLayer extends TileLayer { return tileLayerDescriptor; } - _canSkipSync({ prevDataRequest, nextMeta }) { + getSource(): EMSTMSSource { + return super.getSource() as EMSTMSSource; + } + + _canSkipSync({ + prevDataRequest, + nextMeta, + }: { + prevDataRequest?: DataRequest; + nextMeta: SourceRequestMeta; + }) { if (!prevDataRequest) { return false; } - const prevMeta = prevDataRequest.getMeta(); + const prevMeta = prevDataRequest.getMeta() as SourceRequestMeta; if (!prevMeta) { return false; } @@ -45,7 +80,7 @@ export class VectorTileLayer extends TileLayer { return prevMeta.tileLayerId === nextMeta.tileLayerId; } - async syncData({ startLoading, stopLoading, onLoadError }) { + async syncData({ startLoading, stopLoading, onLoadError }: DataRequestContext) { const nextMeta = { tileLayerId: this.getSource().getTileLayerId() }; const canSkipSync = this._canSkipSync({ prevDataRequest: this.getSourceDataRequest(), @@ -59,7 +94,9 @@ export class VectorTileLayer extends TileLayer { try { startLoading(SOURCE_DATA_REQUEST_ID, requestToken, nextMeta); const styleAndSprites = await this.getSource().getVectorStyleSheetAndSpriteMeta(isRetina()); - const spriteSheetImageData = await loadSpriteSheetImageData(styleAndSprites.spriteMeta.png); + const spriteSheetImageData = styleAndSprites.spriteMeta + ? await loadSpriteSheetImageData(styleAndSprites.spriteMeta.png) + : undefined; const data = { ...styleAndSprites, spriteSheetImageData, @@ -70,7 +107,7 @@ export class VectorTileLayer extends TileLayer { } } - _generateMbId(name) { + _generateMbId(name: string) { return `${this.getId()}_${name}`; } @@ -79,7 +116,7 @@ export class VectorTileLayer extends TileLayer { return `${this.getId()}${DELIMITTER}${this.getSource().getTileLayerId()}${DELIMITTER}`; } - _generateMbSourceId(name) { + _generateMbSourceId(name: string) { return `${this._generateMbSourceIdPrefix()}${name}`; } @@ -88,11 +125,7 @@ export class VectorTileLayer extends TileLayer { if (!sourceDataRequest) { return null; } - const vectorStyleAndSprites = sourceDataRequest.getData(); - if (!vectorStyleAndSprites) { - return null; - } - return vectorStyleAndSprites.vectorStyleSheet; + return (sourceDataRequest.getData() as SourceRequestData)?.vectorStyleSheet; } _getSpriteMeta() { @@ -100,8 +133,7 @@ export class VectorTileLayer extends TileLayer { if (!sourceDataRequest) { return null; } - const vectorStyleAndSprites = sourceDataRequest.getData(); - return vectorStyleAndSprites.spriteMeta; + return (sourceDataRequest.getData() as SourceRequestData)?.spriteMeta; } _getSpriteImageData() { @@ -109,13 +141,12 @@ export class VectorTileLayer extends TileLayer { if (!sourceDataRequest) { return null; } - const vectorStyleAndSprites = sourceDataRequest.getData(); - return vectorStyleAndSprites.spriteSheetImageData; + return (sourceDataRequest.getData() as SourceRequestData)?.spriteSheetImageData; } getMbLayerIds() { const vectorStyle = this._getVectorStyle(); - if (!vectorStyle) { + if (!vectorStyle || !vectorStyle.layers) { return []; } return vectorStyle.layers.map((layer) => this._generateMbId(layer.id)); @@ -123,29 +154,32 @@ export class VectorTileLayer extends TileLayer { getMbSourceIds() { const vectorStyle = this._getVectorStyle(); - if (!vectorStyle) { + if (!vectorStyle || !vectorStyle.sources) { return []; } const sourceIds = Object.keys(vectorStyle.sources); return sourceIds.map((sourceId) => this._generateMbSourceId(sourceId)); } - ownsMbLayerId(mbLayerId) { + ownsMbLayerId(mbLayerId: string) { return mbLayerId.startsWith(this.getId()); } - ownsMbSourceId(mbSourceId) { + ownsMbSourceId(mbSourceId: string) { return mbSourceId.startsWith(this.getId()); } - _makeNamespacedImageId(imageId) { + _makeNamespacedImageId(imageId: string) { const prefix = this.getSource().getSpriteNamespacePrefix() + '/'; return prefix + imageId; } - _requiresPrevSourceCleanup(mbMap) { + _requiresPrevSourceCleanup(mbMap: MbMap) { const sourceIdPrefix = this._generateMbSourceIdPrefix(); const mbStyle = mbMap.getStyle(); + if (!mbStyle.sources) { + return false; + } return Object.keys(mbStyle.sources).some((mbSourceId) => { const doesMbSourceBelongToLayer = this.ownsMbSourceId(mbSourceId); const doesMbSourceBelongToSource = mbSourceId.startsWith(sourceIdPrefix); @@ -153,7 +187,7 @@ export class VectorTileLayer extends TileLayer { }); } - syncLayerWithMB(mbMap) { + syncLayerWithMB(mbMap: MbMap) { const vectorStyle = this._getVectorStyle(); if (!vectorStyle) { return; @@ -162,7 +196,7 @@ export class VectorTileLayer extends TileLayer { this._removeStaleMbSourcesAndLayers(mbMap); let initialBootstrapCompleted = false; - const sourceIds = Object.keys(vectorStyle.sources); + const sourceIds = vectorStyle.sources ? Object.keys(vectorStyle.sources) : []; sourceIds.forEach((sourceId) => { if (initialBootstrapCompleted) { return; @@ -170,20 +204,20 @@ export class VectorTileLayer extends TileLayer { const mbSourceId = this._generateMbSourceId(sourceId); const mbSource = mbMap.getSource(mbSourceId); if (mbSource) { - //if a single source is present, the layer already has bootstrapped with the mbMap + // if a single source is present, the layer already has bootstrapped with the mbMap initialBootstrapCompleted = true; return; } - mbMap.addSource(mbSourceId, vectorStyle.sources[sourceId]); + mbMap.addSource(mbSourceId, vectorStyle.sources![sourceId]); }); if (!initialBootstrapCompleted) { - //sync spritesheet + // sync spritesheet const spriteMeta = this._getSpriteMeta(); if (!spriteMeta) { return; } - const newJson = {}; + const newJson: EmsSpriteSheet = {}; for (const imageId in spriteMeta.json) { if (spriteMeta.json.hasOwnProperty(imageId)) { const namespacedImageId = this._makeNamespacedImageId(imageId); @@ -197,8 +231,9 @@ export class VectorTileLayer extends TileLayer { } addSpriteSheetToMapFromImageData(newJson, imageData, mbMap); - //sync layers - vectorStyle.layers.forEach((layer) => { + // sync layers + const layers = vectorStyle.layers ? vectorStyle.layers : []; + layers.forEach((layer) => { const mbLayerId = this._generateMbId(layer.id); const mbLayer = mbMap.getLayer(mbLayerId); if (mbLayer) { @@ -206,7 +241,10 @@ export class VectorTileLayer extends TileLayer { } const newLayerObject = { ...layer, - source: this._generateMbSourceId(layer.source), + source: + typeof (layer as MbLayer).source === 'string' + ? this._generateMbSourceId((layer as MbLayer).source as string) + : undefined, id: mbLayerId, }; @@ -237,15 +275,35 @@ export class VectorTileLayer extends TileLayer { this._setTileLayerProperties(mbMap); } - _setOpacityForType(mbMap, mbLayer, mbLayerId) { - const opacityProps = MB_STYLE_TYPE_TO_OPACITY[mbLayer.type]; - if (!opacityProps) { - return; + _getOpacityProps(layerType: string): string[] { + if (layerType === 'fill') { + return ['fill-opacity']; + } + + if (layerType === 'line') { + return ['line-opacity']; + } + + if (layerType === 'circle') { + return ['circle-opacity']; } - opacityProps.forEach((opacityProp) => { - if (mbLayer.paint && typeof mbLayer.paint[opacityProp] === 'number') { - const newOpacity = mbLayer.paint[opacityProp] * this.getAlpha(); + if (layerType === 'background') { + return ['background-opacity']; + } + + if (layerType === 'symbol') { + return ['icon-opacity', 'text-opacity']; + } + + return []; + } + + _setOpacityForType(mbMap: MbMap, mbLayer: MbLayer, mbLayerId: string) { + this._getOpacityProps(mbLayer.type).forEach((opacityProp) => { + const mbPaint = mbLayer.paint as { [key: string]: unknown } | undefined; + if (mbPaint && typeof mbPaint[opacityProp] === 'number') { + const newOpacity = (mbPaint[opacityProp] as number) * this.getAlpha(); mbMap.setPaintProperty(mbLayerId, opacityProp, newOpacity); } else { mbMap.setPaintProperty(mbLayerId, opacityProp, this.getAlpha()); @@ -253,21 +311,21 @@ export class VectorTileLayer extends TileLayer { }); } - _setLayerZoomRange(mbMap, mbLayer, mbLayerId) { - let minZoom = this._descriptor.minZoom; + _setLayerZoomRange(mbMap: MbMap, mbLayer: MbLayer, mbLayerId: string) { + let minZoom = this.getMinZoom(); if (typeof mbLayer.minzoom === 'number') { minZoom = Math.max(minZoom, mbLayer.minzoom); } - let maxZoom = this._descriptor.maxZoom; + let maxZoom = this.getMaxZoom(); if (typeof mbLayer.maxzoom === 'number') { maxZoom = Math.min(maxZoom, mbLayer.maxzoom); } mbMap.setLayerZoomRange(mbLayerId, minZoom, maxZoom); } - _setTileLayerProperties(mbMap) { + _setTileLayerProperties(mbMap: MbMap) { const vectorStyle = this._getVectorStyle(); - if (!vectorStyle) { + if (!vectorStyle || !vectorStyle.layers) { return; }