[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>
This commit is contained in:
Nathan Reese 2021-11-08 18:39:14 -07:00 committed by GitHub
parent 7e4ae48efa
commit 7d90bad960
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 142 additions and 102 deletions

View file

@ -466,7 +466,7 @@ export class AbstractLayer implements ILayer {
return null;
}
isBasemap(): boolean {
isBasemap(order: number): boolean {
return false;
}
}

View file

@ -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);
}

View file

@ -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<LayerDescriptor>) {
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;
}
}

View file

@ -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);
}

View file

@ -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';
const MB_STYLE_TYPE_TO_OPACITY = {
fill: ['fill-opacity'],
line: ['line-opacity'],
circle: ['circle-opacity'],
background: ['background-opacity'],
symbol: ['icon-opacity', 'text-opacity'],
};
interface SourceRequestMeta {
tileLayerId: string;
}
// 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<LayerDescriptor>) {
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'];
}
opacityProps.forEach((opacityProp) => {
if (mbLayer.paint && typeof mbLayer.paint[opacityProp] === 'number') {
const newOpacity = mbLayer.paint[opacityProp] * this.getAlpha();
if (layerType === 'line') {
return ['line-opacity'];
}
if (layerType === 'circle') {
return ['circle-opacity'];
}
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;
}