diff --git a/dev-demos/features/tile/rasterData/update.tsx b/dev-demos/features/tile/rasterData/update.tsx new file mode 100644 index 0000000000..772e5b2f92 --- /dev/null +++ b/dev-demos/features/tile/rasterData/update.tsx @@ -0,0 +1,141 @@ +import { RasterLayer, Scene, Source } from '@antv/l7'; +import { GaodeMap } from '@antv/l7-maps'; +import React, { useEffect } from 'react'; +import * as GeoTIFF from 'geotiff'; + +const colorList = [ + '#419bdf', // Water + '#419bdf', + + '#397d49', // Tree + '#397d49', + + '#88b053', // Grass + '#88b053', + + '#7a87c6', // vegetation + '#7a87c6', + + '#e49635', // Crops + '#e49635', + + '#dfc35a', // shrub + '#dfc35a', + + '#c4281b', // Built Area + '#c4281b', + + '#a59b8f', // Bare ground + '#a59b8f', + + '#a8ebff', // Snow + '#a8ebff', + + '#616161', // Clouds + '#616161' +]; +const positions = [ + 0.0, + 0.1, + 0.1, + 0.2, + 0.2, + 0.3, + 0.3, + 0.4, + 0.4, + 0.5, + 0.5, + 0.6, + 0.6, + 0.7, + 0.7, + 0.8, + 0.8, + 0.9, + 0.9, + 1.0 +]; +export default () => { + + useEffect(() => { + const scene = new Scene({ + id: 'map', + stencil: true, + map: new GaodeMap({ + center: [ 116, 27 ], + zoom: 6, + style: 'dark' + }) + }); + + scene.on('loaded', () => { + fetch( + 'https://gw.alipayobjects.com/os/bmw-prod/fccd80c0-2611-49f9-9a9f-e2a4dd12226f.json' + ) + .then(res => res.json()) + .then(maskData => { + const layer = new RasterLayer({ + mask: true, + maskfence: maskData + }); + + const tileSource = new Source('https://ganos.oss-cn-hangzhou.aliyuncs.com/m2/l7/tiff_jx/{z}/{x}/{y}.tiff', + { + parser: { + type: 'rasterTile', + dataType: 'arraybuffer', + tileSize: 256, + maxZoom: 13.1, + format: async data => { + const tiff = await GeoTIFF.fromArrayBuffer(data); + const image = await tiff.getImage(); + const width = image.getWidth(); + const height = image.getHeight(); + const values = await image.readRasters(); + return { rasterData: values[0], width, height }; + } + } + }); + + layer.source(tileSource) + .style({ + domain: [ 0.001, 11.001 ], + clampLow: false, + rampColors: { + colors: colorList, + positions + } + }); + + scene.addLayer(layer); + + setTimeout(() => { + layer.style({ + rampColors: { + colors: ['#f00', '#ff0'], + positions: [0, 1] + } + }); + scene.render() + }, 2000) + }); + }); + + + + return () => { + scene.destroy(); + }; + }, []); + + return ( +
+ ); +}; diff --git a/dev-demos/features/tile/rasterDataUpdate.md b/dev-demos/features/tile/rasterDataUpdate.md new file mode 100644 index 0000000000..1a5c854531 --- /dev/null +++ b/dev-demos/features/tile/rasterDataUpdate.md @@ -0,0 +1,4 @@ +### Raster - RasterData - Update +数据栅格 update +#### 加载 tiff + \ No newline at end of file diff --git a/dev-demos/features/tile/vector.md b/dev-demos/features/tile/vector.md index c68b2304c0..b97dda4d19 100644 --- a/dev-demos/features/tile/vector.md +++ b/dev-demos/features/tile/vector.md @@ -1,13 +1,13 @@ ### Vector - + #### vector line -#### geojson-vt - + -#### debugLayer - \ No newline at end of file + \ No newline at end of file diff --git a/dev-demos/features/tile/vector/geojson-vt.tsx b/dev-demos/features/tile/vector/geojson-vt.tsx index 3352cec557..99ab111780 100644 --- a/dev-demos/features/tile/vector/geojson-vt.tsx +++ b/dev-demos/features/tile/vector/geojson-vt.tsx @@ -46,7 +46,8 @@ export default () => { }) .source(source) .color('COLOR') - .active(true) + // .active(true) + .select(true) .style({ opacity: 0.6, }); diff --git a/dev-demos/features/tile/vector/line.tsx b/dev-demos/features/tile/vector/line.tsx index 14c90e9c79..89cf688bae 100644 --- a/dev-demos/features/tile/vector/line.tsx +++ b/dev-demos/features/tile/vector/line.tsx @@ -1,7 +1,7 @@ // @ts-ignore import { Scene, LineLayer } from '@antv/l7'; // @ts-ignore -import { Mapbox } from '@antv/l7-maps'; +import { Map } from '@antv/l7-maps'; import React, { useEffect } from 'react'; export default () => { @@ -9,10 +9,8 @@ export default () => { const scene = new Scene({ id: 'line', stencil: true, - map: new Mapbox({ + map: new Map({ center: [121.268, 30.3628], - pitch: 0, - style: 'blank', zoom: 4, }), }); diff --git a/packages/core/src/services/layer/ILayerService.ts b/packages/core/src/services/layer/ILayerService.ts index 98b7195aad..1d4b966d9e 100644 --- a/packages/core/src/services/layer/ILayerService.ts +++ b/packages/core/src/services/layer/ILayerService.ts @@ -172,17 +172,6 @@ export interface ISubLayerInitOptions { workerEnabled?: boolean; } -export interface ITilePickManager { - isLastPicked: boolean; - on(type: string, cb: (option: any) => void): void; - normalRender(layers: ILayer[]): void; - beforeHighlight(pickedColors: any): void; - beforeSelect(pickedColors: any): void; - clearPick(): void; - pickRender(layers: ILayer[], target: IInteractionTarget): boolean; - destroy(): void; -} - export interface IBaseTileLayerManager { sourceLayer: string; parent: ILayer; @@ -191,24 +180,39 @@ export interface IBaseTileLayerManager { createTile(tile: Tile): { layers: ILayer[]; layerIDList: string[] }; addChild(layer: ILayer): void; - addChilds(layers: ILayer[]): void; - getChilds(layerIDList: string[]): ILayer[]; + addChildren(layers: ILayer[]): void; + getChildren(layerIDList: string[]): ILayer[]; removeChild(layer: ILayer): void; - removeChilds(layerIDList: string[], refresh?: boolean): void; + removeChildren(layerIDList: string[], refresh?: boolean): void; clearChild(): void; hasChild(layer: ILayer): boolean; render(isPicking?: boolean): void; destroy(): void; } +export interface ITileRenderService { + render(layers: ILayer[]): void; + renderMask(layers: ILayer): void; +} + +export interface ITilePickService { + isLastPicked: boolean; + on(type: string, cb: (option: any) => void): void; + beforeHighlight(pickedColors: any): void; + beforeSelect(pickedColors: any): void; + clearPick(): void; + pick(layers: ILayer[], target: IInteractionTarget): boolean; + destroy(): void; +} + + export interface ITileLayerManager extends IBaseTileLayerManager{ - tilePickManager: ITilePickManager; + tilePickService: ITilePickService; pickLayers(target: IInteractionTarget): boolean; destroy(): void; } export interface IBaseTileLayer { - type: string; sourceLayer: string; parent: ILayer; tileLayerManager: IBaseTileLayerManager; diff --git a/packages/layers/src/index.ts b/packages/layers/src/index.ts index 6fead23f06..833dc45868 100644 --- a/packages/layers/src/index.ts +++ b/packages/layers/src/index.ts @@ -16,7 +16,7 @@ import EarthLayer from './earth'; import MaskLayer from './mask'; import WindLayer from './wind'; -import TileDebugLayer from './tile/tileTest'; +import TileDebugLayer from './tile/tileFactory/layers/tileTest'; // import ConfigSchemaValidationPlugin from './plugins/ConfigSchemaValidationPlugin'; import DataMappingPlugin from './plugins/DataMappingPlugin'; diff --git a/packages/layers/src/point/models/earthFill.ts b/packages/layers/src/point/models/earthFill.ts index 8c7c1eb468..a4c0128155 100644 --- a/packages/layers/src/point/models/earthFill.ts +++ b/packages/layers/src/point/models/earthFill.ts @@ -206,9 +206,6 @@ export default class FillModel extends BaseModel { size: 1, update: ( feature: IEncodeFeature, - featureIdx: number, - vertex: number[], - attributeIdx: number, ) => { const { size = 5 } = feature; return Array.isArray(size) ? [size[0]] : [size as number]; @@ -231,9 +228,6 @@ export default class FillModel extends BaseModel { size: 1, update: ( feature: IEncodeFeature, - featureIdx: number, - vertex: number[], - attributeIdx: number, ) => { const { shape = 2 } = feature; const shape2d = this.layer.getLayerConfig().shape2d as string[]; diff --git a/packages/layers/src/tile/interaction/TilePickService.ts b/packages/layers/src/tile/interaction/TilePickService.ts new file mode 100644 index 0000000000..c1ef7d5360 --- /dev/null +++ b/packages/layers/src/tile/interaction/TilePickService.ts @@ -0,0 +1,122 @@ +import { + ILayer, + IPickingService, + IRendererService, + IInteractionTarget, + ITileRenderService, + } from '@antv/l7-core'; +import { EventEmitter } from 'eventemitter3'; + +export class TilePickService extends EventEmitter{ + public isLastPicked: boolean = false; + private rendererService: IRendererService; + private pickingService: IPickingService; + private children: ILayer[]; + private parent: ILayer; + private tileRenderService: ITileRenderService; + constructor( + parent: ILayer, + rendererService: IRendererService, + pickingService: IPickingService, + children: ILayer[], + tileRenderService: ITileRenderService + ) { + super(); + this.parent = parent; + this.rendererService = rendererService; + this.pickingService = pickingService; + this.children = children; + this.tileRenderService = tileRenderService; + } + + + + public pick(layers: ILayer[], target: IInteractionTarget) { + // Tip: 在进行拾取渲染的时候也需要先渲染一遍父组件然后再渲染子组件 + // 如需要在 栅格瓦片存在 Mask 的时候发生的拾取,那么就需要先渲染父组件(渲染父组件的帧缓冲) + this.tileRenderService.renderMask(this.parent); + const isPicked = layers + .filter( + (layer) => + this.parent.needPick(target.type) && + layer.inited && + layer.isVisible(), + ) + .some((layer) => { + layer.hooks.beforePickingEncode.call(); + if (layer.masks.length > 0) { + // 清除上一次的模版缓存 + this.rendererService.clear({ + stencil: 0, + depth: 1, + framebuffer: null, + }); + + layer.masks.map((m: ILayer) => { + m.hooks.beforeRender.call(); + m.render(); + m.hooks.afterRender.call(); + }); + } + layer.renderModels(true); + layer.hooks.afterPickingEncode.call(); + const layerPicked = this.pickingService.pickFromPickingFBO( + layer, + target, + ); + // RasterLayer 不参与拾取后的 shader 计算 + if (layerPicked && this.parent.type !== 'RasterLayer') { + this.emit('pick', { + type: target.type, + pickedColors: this.pickingService.pickedColors, + layer, + }); + this.pickingService.pickedTileLayers = [this.parent]; + } + + return layerPicked; + }); + if ( + this.parent.type !== 'RasterLayer' && + !isPicked && + this.isLastPicked && + target.type !== 'click' + ) { + // 只有上一次有被高亮选中,本次未选中的时候才需要清除选中状态 + this.pickingService.pickedTileLayers = []; + this.emit('unpick', {}); + this.beforeHighlight([0, 0, 0]); + } + this.isLastPicked = isPicked; + return isPicked; + } + + public clearPick() { + this.children + .filter((child) => child.inited && child.isVisible()) + .map((layer) => { + layer.hooks.beforeSelect.call([0, 0, 0]); + }); + this.pickingService.pickedTileLayers = []; + } + + public beforeHighlight(pickedColors: any) { + this.children + .filter((child) => child.inited && child.isVisible()) + .map((child) => { + child.hooks.beforeHighlight.call(pickedColors); + }); + } + + public beforeSelect(pickedColors: any) { + this.children + .filter((child) => child.inited && child.isVisible()) + .map((layer) => { + layer.hooks.beforeSelect.call(pickedColors); + }); + } + + public destroy(): void { + this.removeAllListeners(); + } +} \ No newline at end of file diff --git a/packages/layers/src/tile/interaction/getFeatureData.ts b/packages/layers/src/tile/interaction/getFeatureData.ts new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/layers/src/tile/interaction/getRasterData.ts b/packages/layers/src/tile/interaction/getRasterData.ts new file mode 100644 index 0000000000..527db120c9 --- /dev/null +++ b/packages/layers/src/tile/interaction/getRasterData.ts @@ -0,0 +1,81 @@ +import { + IMapService, + IRendererService + } from '@antv/l7-core'; +import { DOM, Tile } from '@antv/l7-utils'; + +export function readRasterValue( + tile: Tile, + mapService: IMapService, + x: number, + y: number, + ) { + const bbox = tile?.bboxPolygon?.bbox || [0, 0, 10, -10]; + + const [minLng = 0, minLat = 0, maxLng = 10, maxLat = -10] = bbox; + + const tileXY = mapService.lngLatToContainer([minLng, minLat]); + const tileMaxXY = mapService.lngLatToContainer([maxLng, maxLat]); + + const tilePixelWidth = tileMaxXY.x - tileXY.x; + const tilePixelHeight = tileXY.y - tileMaxXY.y; + const pos = [ + (x - tileXY.x) / tilePixelWidth, // x + (y - tileMaxXY.y) / tilePixelHeight, // y + ]; + + const tileWidth = tile?.data?.width || 1; + const tileHeight = tile?.data?.height || 1; + + const indexX = Math.floor(pos[0] * tileWidth); + const indexY = Math.floor(pos[1] * tileHeight); + const index = Math.max(0, indexY - 1) * tileWidth + indexX; + + const data = tile?.data?.data[index]; + + return data; + } + + export function readPixel( + x: number, + y: number, + rendererService: IRendererService, + ) { + const { readPixels, getContainer } = rendererService; + const xInDevicePixel = x * DOM.DPR; + const yInDevicePixel = y * DOM.DPR; + let { width, height } = getContainerSize( + getContainer() as HTMLCanvasElement | HTMLElement, + ); + width *= DOM.DPR; + height *= DOM.DPR; + if ( + xInDevicePixel > width - 1 * DOM.DPR || + xInDevicePixel < 0 || + yInDevicePixel > height - 1 * DOM.DPR || + yInDevicePixel < 0 + ) { + return false; + } + + const pickedColors = readPixels({ + x: Math.floor(xInDevicePixel), + // 视口坐标系原点在左上,而 WebGL 在左下,需要翻转 Y 轴 + y: Math.floor(height - (y + 1) * DOM.DPR), + width: 1, + height: 1, + data: new Uint8Array(1 * 1 * 4), + }); + return pickedColors; + } + + function getContainerSize(container: HTMLCanvasElement | HTMLElement) { + if ((container as HTMLCanvasElement).getContext) { + return { + width: (container as HTMLCanvasElement).width / DOM.DPR, + height: (container as HTMLCanvasElement).height / DOM.DPR, + }; + } else { + return container.getBoundingClientRect(); + } + } \ No newline at end of file diff --git a/packages/layers/src/tile/interaction/utils.ts b/packages/layers/src/tile/interaction/utils.ts new file mode 100644 index 0000000000..23efd906c6 --- /dev/null +++ b/packages/layers/src/tile/interaction/utils.ts @@ -0,0 +1,106 @@ +import { + ILayer, + } from '@antv/l7-core'; +import { decodePickingColor } from '@antv/l7-utils'; + +export function clearPickState(layers: ILayer[]) { + layers + .filter((layer) => layer.inited && layer.isVisible()) + .filter((layer) => layer.getCurrentSelectedId() !== null) + .map((layer) => { + selectFeature(layer, new Uint8Array([0, 0, 0, 0])); + layer.setCurrentSelectedId(null); + }); +} + +export function setSelect(layers: ILayer[], pickedColors: any, renderList: ILayer[]) { + const selectedId = decodePickingColor(pickedColors); + let pickColor + layers.map((layer) => { + if ( + layer.getCurrentSelectedId() === null || + selectedId !== layer.getCurrentSelectedId() + ) { + selectFeature(layer, pickedColors); + layer.setCurrentSelectedId(selectedId); + pickColor = pickedColors; + } else { + selectFeature(layer, new Uint8Array([0, 0, 0, 0])); // toggle select + layer.setCurrentSelectedId(null); + pickColor = null; + } + }); + // unselect normal layer + renderList + .filter( + (layer) => + layer.inited && + !layer.isVector && + layer.isVisible() && + layer.needPick('click'), + ) + .filter((layer) => layer.getCurrentSelectedId() !== null) + .map((layer) => { + selectFeature(layer, new Uint8Array([0, 0, 0, 0])); + layer.setCurrentSelectedId(null); + }); + return pickColor; +} + +export function setHighlight(layers: ILayer[], pickedColors: any) { + const pickId = decodePickingColor(pickedColors); + layers.filter((layer) => layer.inited && layer.isVisible() && layer.isVector) + // Tip: 使用 vectorLayer 上的 pickID 优化高亮操作(过滤重复操作) + // @ts-ignore + .filter((layer) => layer.getPickID() !== pickId) + .map((layer) => { + // @ts-ignore + layer.setPickID(pickId); + layer.hooks.beforeHighlight.call(pickedColors); + }); +} + +export function setPickState(layers: ILayer[], pickColors: { select: any, active: any }) { + if (pickColors.select) { + layers.map((layer) => { + // if(layer.modelLoaded) { + // selectFeature(layer, this.pickColors.select); + // } else { + // layer.once('modelLoaded', () => { + // selectFeature(layer, this.pickColors.select); + // }) + // } + selectFeature(layer, pickColors.select); + }); + } + + if (pickColors.active) { + layers + .filter((layer) => layer.inited && layer.isVisible()) + .map((layer) => { + layer.hooks.beforeHighlight.call(pickColors.active); + }); + } +} + +export function selectFeature(layer: ILayer, pickedColors: Uint8Array | undefined) { + // @ts-ignore + const [r, g, b] = pickedColors; + layer.hooks.beforeSelect.call([r, g, b]); +} + +export function setFeatureSelect(color: any, layers: ILayer[]) { + const id = decodePickingColor(color); + layers.map((layer) => { + selectFeature(layer, color); + layer.setCurrentSelectedId(id); + }); +} + +export function setFeatureActive(color: any, layers: ILayer[]) { + const id = decodePickingColor(color); + layers.map((layer) => { + layer.hooks.beforeHighlight.call(color); + layer.setCurrentPickId(id); + }); +} \ No newline at end of file diff --git a/packages/layers/src/tile/interface.ts b/packages/layers/src/tile/interface.ts index 58d0f98117..510abdcb7e 100644 --- a/packages/layers/src/tile/interface.ts +++ b/packages/layers/src/tile/interface.ts @@ -2,10 +2,8 @@ import { ILayer, IMapService, IRendererService, - IScaleValue, ISource, ISubLayerInitOptions, - ScaleAttributeType, } from '@antv/l7-core'; import { Tile } from '@antv/l7-utils'; @@ -48,9 +46,4 @@ export interface ITileFactory { createLayer(option: ILayerTileConfig): ILayer; updateStyle(styles: ITileStyles): string; - setStyleAttributeField( - layer: ILayer, - type: ScaleAttributeType, - value: IScaleValue, - ): void; } diff --git a/packages/layers/src/tile/manager/baseTileManager.ts b/packages/layers/src/tile/manager/base.ts similarity index 82% rename from packages/layers/src/tile/manager/baseTileManager.ts rename to packages/layers/src/tile/manager/base.ts index 64a5f515a6..3c93492ef9 100644 --- a/packages/layers/src/tile/manager/baseTileManager.ts +++ b/packages/layers/src/tile/manager/base.ts @@ -6,6 +6,7 @@ import { } from '@antv/l7-core'; import { Tile } from '@antv/l7-utils'; import { ITileFactory, getTileFactory, TileType } from '../tileFactory'; +import { registerLayers } from '../utils'; export class TileManager { public sourceLayer: string; public parent: ILayer; @@ -16,18 +17,27 @@ export class TileManager { protected initOptions: ISubLayerInitOptions; public createTile(tile: Tile) { - return this.tileFactory.createTile(tile, this.initOptions); + const layerCollections = this.tileFactory.createTile(tile, this.initOptions); + // // regist layer + registerLayers(this.parent, layerCollections.layers); + + layerCollections.layers.map(layer => { + layer.once('modelLoaded', () => { + tile.layerLoad(); + }) + }) + return layerCollections; } public addChild(layer: ILayer) { this.children.push(layer); } - public addChilds(layers: ILayer[]) { + public addChildren(layers: ILayer[]) { this.children.push(...layers); } - public removeChilds(layerIDList: string[], refresh = true) { + public removeChildren(layerIDList: string[], refresh = true) { const remveLayerList: ILayer[] = []; const cacheLayerList: ILayer[] = []; this.children.filter((child) => { @@ -47,7 +57,7 @@ export class TileManager { layer.destroy(); } - public getChilds(layerIDList: string[]) { + public getChildren(layerIDList: string[]) { return this.children.filter((child) => layerIDList.includes(child.id)); } diff --git a/packages/layers/src/tile/manager/tileLayerManager.ts b/packages/layers/src/tile/manager/layerManager.ts similarity index 51% rename from packages/layers/src/tile/manager/tileLayerManager.ts rename to packages/layers/src/tile/manager/layerManager.ts index 659f23567f..a145478ae9 100644 --- a/packages/layers/src/tile/manager/tileLayerManager.ts +++ b/packages/layers/src/tile/manager/layerManager.ts @@ -6,18 +6,24 @@ import { IRendererService, ISubLayerInitOptions, ITileLayerManager, - ITilePickManager, + ITilePickService, + ITileRenderService, ITransform, ScaleAttributeType, } from '@antv/l7-core'; -import { TileManager } from './baseTileManager'; +import { TileManager } from './base'; import { generateColorRamp, IColorRamp } from '@antv/l7-utils'; -import { getLayerShape, getMaskValue, updateLayersConfig } from '../utils'; -import TileConfigManager, { ITileConfigManager } from './tileConfigManager'; -import TilePickManager from './tilePickerManager'; +import { getLayerShape, getMaskValue } from '../utils'; +import { TileStyleService, ITileStyleService } from '../style/TileStyleService'; +import { TilePickService } from '../interaction/TilePickService'; + +import { TileRenderService } from '../render/TileRenderService'; +import { styles, IStyles, Attributes } from '../style/constants'; +import { updateTexture, updateLayersConfig, setStyleAttributeField } from '../style/utils'; export class TileLayerManager extends TileManager implements ITileLayerManager { - public tilePickManager: ITilePickManager; - public tileConfigManager: ITileConfigManager; + public tilePickService: ITilePickService; + public tileStyleService: ITileStyleService; + public tileRenderService: ITileRenderService private transforms: ITransform[]; constructor( parent: ILayer, @@ -33,13 +39,16 @@ export class TileLayerManager extends TileManager implements ITileLayerManager { this.rendererService = rendererService; this.transforms = transforms; - this.tilePickManager = new TilePickManager( + this.tileRenderService = new TileRenderService(rendererService); + + this.tilePickService = new TilePickService( parent, rendererService, pickingService, this.children, + this.tileRenderService ); - this.tileConfigManager = new TileConfigManager(); + this.tileStyleService = new TileStyleService(); this.setSubLayerInitOption(); this.setConfigListener(); @@ -47,12 +56,12 @@ export class TileLayerManager extends TileManager implements ITileLayerManager { } public render(): void { - this.tileConfigManager?.checkConfig(this.parent); - this.tilePickManager?.normalRender(this.children); + this.tileStyleService.checkConfig(this.parent); + this.tileRenderService.render(this.children); } public pickLayers(target: IInteractionTarget) { - return this.tilePickManager?.pickRender(this.children, target); + return this.tilePickService.pick(this.children, target); } private setSubLayerInitOption() { @@ -88,11 +97,11 @@ export class TileLayerManager extends TileManager implements ITileLayerManager { pixelConstantRGB = 0.1, } = this.parent.getLayerConfig() as ISubLayerInitOptions; - const colorValue = this.tileConfigManager.getAttributeScale( + const colorValue = this.tileStyleService.getAttributeScale( this.parent, 'color', ); - const sizeValue = this.tileConfigManager.getAttributeScale( + const sizeValue = this.tileStyleService.getAttributeScale( this.parent, 'size', ); @@ -146,92 +155,55 @@ export class TileLayerManager extends TileManager implements ITileLayerManager { } } - private setConfigListener() { - // RasterLayer PolygonLayer LineLayer PointLayer - // All Tile Layer Need Listen - this.tileConfigManager.setConfig('opacity', this.initOptions.opacity); - this.tileConfigManager.setConfig('zIndex', this.initOptions.zIndex); - this.tileConfigManager.setConfig('mask', this.initOptions.mask); - - if (this.parent.type === 'RasterLayer') { - // Raster Tile Layer Need Listen - this.tileConfigManager.setConfig( - 'rampColors', - this.initOptions.rampColors, - ); - this.tileConfigManager.setConfig('domain', this.initOptions.domain); - this.tileConfigManager.setConfig('clampHigh', this.initOptions.clampHigh); - this.tileConfigManager.setConfig('clampLow', this.initOptions.clampLow); - - this.tileConfigManager.setConfig( - 'pixelConstant', - this.initOptions.pixelConstant, - ); - this.tileConfigManager.setConfig( - 'pixelConstantR', - this.initOptions.pixelConstantR, - ); - this.tileConfigManager.setConfig( - 'pixelConstantG', - this.initOptions.pixelConstantG, - ); - this.tileConfigManager.setConfig( - 'pixelConstantB', - this.initOptions.pixelConstantB, - ); - this.tileConfigManager.setConfig( - 'pixelConstantRGB', - this.initOptions.pixelConstantRGB, - ); - } else { - // Vector Tile Layer Need Listen - this.tileConfigManager.setConfig('stroke', this.initOptions.stroke); - this.tileConfigManager.setConfig( - 'strokeWidth', - this.initOptions.strokeWidth, - ); - this.tileConfigManager.setConfig( - 'strokeOpacity', - this.initOptions.strokeOpacity, - ); - this.tileConfigManager.setConfig( - 'color', - this.parent.getAttribute('color')?.scale, - ); - this.tileConfigManager.setConfig( - 'shape', - this.parent.getAttribute('shape')?.scale, - ); - this.tileConfigManager.setConfig( - 'size', - this.parent.getAttribute('size')?.scale, - ); + private getInitOptionValue(field: string) { + switch(field) { + case 'color': return this.parent.getAttribute('color')?.scale; + case 'shape': return this.parent.getAttribute('shape')?.scale; + case 'size': return this.parent.getAttribute('size')?.scale; + // @ts-ignore + default: return this.initOptions[field]; } + } - this.tileConfigManager.on('updateConfig', (updateConfigs) => { + private setInitOptionValue(field: string, value: any) { + // @ts-ignore + this.initOptions[field] = value; + } + + private setConfigListener() { + const styleConfigs = styles[this.parent.type as IStyles] || []; + styleConfigs.map(style => { + this.tileStyleService.setConfig(style, this.getInitOptionValue(style)); + }) + + this.tileStyleService.on('updateConfig', (updateConfigs) => { updateConfigs.map((key: string) => { - this.updateStyle(key); - return ''; + return this.updateStyle(key); }); }); } - private updateStyle(style: string) { - let updateValue = null; - if (['size', 'color', 'shape'].includes(style)) { + private updateAttribute(style: string) { + if(Attributes.includes(style)) { const scaleValue = this.parent.getAttribute(style)?.scale; if (!scaleValue) { return; } - updateValue = scaleValue; this.children.map((child) => { - this.tileFactory.setStyleAttributeField( + return setStyleAttributeField( child, + this.parent, style as ScaleAttributeType, scaleValue, ); - return ''; }); + } + } + + private updateStyle(style: string) { + let updateValue = null; + if (Attributes.includes(style)) { + this.updateAttribute(style); } else { const layerConfig = this.parent.getLayerConfig() as ISubLayerInitOptions; if (!(style in layerConfig)) { @@ -240,24 +212,20 @@ export class TileLayerManager extends TileManager implements ITileLayerManager { // @ts-ignore const config = layerConfig[style]; updateValue = config; - updateLayersConfig(this.children, style, config); - if (style === 'rampColors' && config) { - const { createTexture2D } = this.rendererService; - const imageData = generateColorRamp(config as IColorRamp) as ImageData; - this.initOptions.colorTexture = createTexture2D({ - data: imageData.data, - width: imageData.width, - height: imageData.height, - flipY: false, - }); - updateLayersConfig(this.children, 'colorTexture', this.initOptions.colorTexture); + switch(style) { + case 'rampColors': + const texture = updateTexture(config, this.children, this.rendererService) + this.initOptions.colorTexture = texture; + break; + default: + updateLayersConfig(this.children, style, config); } } - // @ts-ignore - this.initOptions[style] = updateValue; + + this.setInitOptionValue(style, updateValue); } public destroy(): void { - this.tilePickManager.destroy(); + this.tilePickService.destroy(); } } diff --git a/packages/layers/src/tile/manager/baseMapTileLayerManager.ts b/packages/layers/src/tile/manager/mapLayerManager.ts similarity index 98% rename from packages/layers/src/tile/manager/baseMapTileLayerManager.ts rename to packages/layers/src/tile/manager/mapLayerManager.ts index cd16a7f1a1..2b598049be 100644 --- a/packages/layers/src/tile/manager/baseMapTileLayerManager.ts +++ b/packages/layers/src/tile/manager/mapLayerManager.ts @@ -5,7 +5,7 @@ import { ISubLayerInitOptions, IBaseTileLayerManager, } from '@antv/l7-core'; -import { TileManager } from './baseTileManager'; +import { TileManager } from './base'; import { getLayerShape, getMaskValue } from '../utils'; export class BaseMapTileLayerManager extends TileManager implements IBaseTileLayerManager { // only support vector layer @@ -91,5 +91,4 @@ export class BaseMapTileLayerManager extends TileManager implements IBaseTileLay workerEnabled, }; } - } diff --git a/packages/layers/src/tile/manager/tilePickerManager.ts b/packages/layers/src/tile/manager/tilePickerManager.ts deleted file mode 100644 index 90c3bb5bca..0000000000 --- a/packages/layers/src/tile/manager/tilePickerManager.ts +++ /dev/null @@ -1,169 +0,0 @@ -import { - IInteractionTarget, - ILayer, - IPickingService, - IRendererService, - ITilePickManager, -} from '@antv/l7-core'; -import { EventEmitter } from 'eventemitter3'; -export default class TilePickManager extends EventEmitter - implements ITilePickManager { - public isLastPicked: boolean = false; - private rendererService: IRendererService; - private pickingService: IPickingService; - private children: ILayer[]; - private parent: ILayer; - - constructor( - parent: ILayer, - rendererService: IRendererService, - pickingService: IPickingService, - children: ILayer[], - ) { - super(); - this.parent = parent; - this.rendererService = rendererService; - this.pickingService = pickingService; - this.children = children; - } - - /** - * - * @param layers - */ - public normalRender(layers: ILayer[]) { - layers - .filter((layer) => layer.inited) - .filter((layer) => layer.isVisible()) - .map((layer) => { - layer.hooks.beforeRenderData.call(); - layer.hooks.beforeRender.call(); - if (layer.masks.length > 0) { - // 清除上一次的模版缓存 - this.rendererService.clear({ - stencil: 0, - depth: 1, - framebuffer: null, - }); - layer.masks.map((m: ILayer) => { - m.hooks.beforeRenderData.call(); - m.hooks.beforeRender.call(); - m.render(); - m.hooks.afterRender.call(); - }); - } - layer.render(); - layer.hooks.afterRender.call(); - }); - } - - public pickRender(layers: ILayer[], target: IInteractionTarget) { - // Tip: 在进行拾取渲染的时候也需要先渲染一遍父组件然后再渲染子组件 - // 如需要在 栅格瓦片存在 Mask 的时候发生的拾取,那么就需要先渲染父组件(渲染父组件的帧缓冲) - if (this.parent.type === 'RasterLayer') { - this.renderMask(this.parent); - } - const isPicked = layers - .filter( - (layer) => - this.parent.needPick(target.type) && - layer.inited && - layer.isVisible(), - ) - .some((layer) => { - layer.hooks.beforePickingEncode.call(); - if (layer.masks.length > 0) { - // 清除上一次的模版缓存 - this.rendererService.clear({ - stencil: 0, - depth: 1, - framebuffer: null, - }); - - layer.masks.map((m: ILayer) => { - m.hooks.beforeRender.call(); - m.render(); - m.hooks.afterRender.call(); - }); - } - layer.renderModels(true); - layer.hooks.afterPickingEncode.call(); - const layerPicked = this.pickingService.pickFromPickingFBO( - layer, - target, - ); - // RasterLayer 不参与拾取后的 shader 计算 - if (layerPicked && this.parent.type !== 'RasterLayer') { - this.emit('pick', { - type: target.type, - pickedColors: this.pickingService.pickedColors, - layer, - }); - this.pickingService.pickedTileLayers = [this.parent]; - } - - return layerPicked; - }); - if ( - this.parent.type !== 'RasterLayer' && - !isPicked && - this.isLastPicked && - target.type !== 'click' - ) { - // 只有上一次有被高亮选中,本次未选中的时候才需要清除选中状态 - this.pickingService.pickedTileLayers = []; - this.emit('unpick', {}); - this.beforeHighlight([0, 0, 0]); - } - this.isLastPicked = isPicked; - return isPicked; - } - - public clearPick() { - this.children - .filter((child) => child.inited && child.isVisible()) - .map((layer) => { - layer.hooks.beforeSelect.call([0, 0, 0]); - }); - this.pickingService.pickedTileLayers = []; - } - - public beforeHighlight(pickedColors: any) { - this.children - .filter((child) => child.inited && child.isVisible()) - .map((child) => { - child.hooks.beforeHighlight.call(pickedColors); - }); - } - - public beforeSelect(pickedColors: any) { - this.children - .filter((child) => child.inited && child.isVisible()) - .map((layer) => { - layer.hooks.beforeSelect.call(pickedColors); - }); - } - - protected renderMask(layer: ILayer) { - if (layer.inited && layer.isVisible()) { - layer.hooks.beforeRender.call(); - if (layer.masks.length > 0) { - this.rendererService.clear({ - stencil: 0, - depth: 1, - framebuffer: null, - }); - layer.masks.map((m: ILayer) => { - m.hooks.beforeRender.call(); - m.render(); - m.hooks.afterRender.call(); - }); - } - layer.hooks.afterRender.call(); - } - } - - public destroy(): void { - this.removeAllListeners(); - } -} diff --git a/packages/layers/src/tile/models/tileModel.ts b/packages/layers/src/tile/models/tileModel.ts index 375c2d24fe..d9282cff29 100644 --- a/packages/layers/src/tile/models/tileModel.ts +++ b/packages/layers/src/tile/models/tileModel.ts @@ -1,7 +1,7 @@ import { IModelUniform } from '@antv/l7-core'; import BaseModel from '../../core/BaseModel'; -import { TMSTileLayer } from '../tmsTileLayer'; -import { TMSBaseMapTileLayer } from '../tmsMapTileLayer'; +import { TileLayer } from '../tileLayer/TileLayer'; +import { MapTileLayer } from '../tileLayer/MapTileLayer'; export default class TileModel extends BaseModel { public getUninforms(): IModelUniform { return {}; @@ -10,9 +10,9 @@ export default class TileModel extends BaseModel { private getTileLayer(usage?: string) { switch(usage) { case 'basemap': - return TMSBaseMapTileLayer; + return MapTileLayer; default: - return TMSTileLayer; + return TileLayer; } } diff --git a/packages/layers/src/tile/render/TileRenderService.ts b/packages/layers/src/tile/render/TileRenderService.ts new file mode 100644 index 0000000000..4f2687b749 --- /dev/null +++ b/packages/layers/src/tile/render/TileRenderService.ts @@ -0,0 +1,55 @@ +import { ILayer, IRendererService, ITileRenderService } from '@antv/l7-core'; +/** + * 主要用于瓦片图层队列的渲染 + */ +export class TileRenderService implements ITileRenderService{ + private rendererService: IRendererService; + + constructor(rendererService: IRendererService) { + this.rendererService = rendererService; + } + + public render(layers: ILayer[]) { + layers + .filter((layer) => layer.inited) + .filter((layer) => layer.isVisible()) + .map((layer) => { + layer.hooks.beforeRenderData.call(); + layer.hooks.beforeRender.call(); + if (layer.masks.length > 0) { + // 清除上一次的模版缓存 + this.rendererService.clear({ + stencil: 0, + depth: 1, + framebuffer: null, + }); + layer.masks.map((m: ILayer) => { + m.hooks.beforeRenderData.call(); + m.hooks.beforeRender.call(); + m.render(); + m.hooks.afterRender.call(); + }); + } + layer.render(); + layer.hooks.afterRender.call(); + }); + } + + public renderMask(layer: ILayer) { + + if (layer.inited && layer.isVisible() && layer.masks.length > 0) { + layer.hooks.beforeRender.call(); + this.rendererService.clear({ + stencil: 0, + depth: 1, + framebuffer: null, + }); + layer.masks.map((m: ILayer) => { + m.hooks.beforeRender.call(); + m.render(); + m.hooks.afterRender.call(); + }); + layer.hooks.afterRender.call(); + } + } +} \ No newline at end of file diff --git a/packages/layers/src/tile/manager/tileConfigManager.ts b/packages/layers/src/tile/style/TileStyleService.ts similarity index 95% rename from packages/layers/src/tile/manager/tileConfigManager.ts rename to packages/layers/src/tile/style/TileStyleService.ts index c8a10315e3..810b69aab6 100644 --- a/packages/layers/src/tile/manager/tileConfigManager.ts +++ b/packages/layers/src/tile/style/TileStyleService.ts @@ -1,14 +1,14 @@ import { ILayer, IScaleValue, ISubLayerInitOptions } from '@antv/l7-core'; import EventEmitter from 'eventemitter3'; import { isEqual } from 'lodash'; -export interface ITileConfigManager { +export interface ITileStyleService { setConfig(key: string, value: any): void; checkConfig(layer: ILayer): void; on(event: string, fn: (...args: any[]) => void): void; getAttributeScale(layer: ILayer, name: string): IScaleValue; } -export default class TileConfigManager extends EventEmitter { +export class TileStyleService extends EventEmitter { public cacheConfig: Map; public checkConfigList: string[] = []; constructor() { diff --git a/packages/layers/src/tile/style/constants.ts b/packages/layers/src/tile/style/constants.ts new file mode 100644 index 0000000000..0362e4e9a3 --- /dev/null +++ b/packages/layers/src/tile/style/constants.ts @@ -0,0 +1,58 @@ +/** + * 瓦片图层的样式 + */ + +export const Attributes = ['size', 'color', 'shape']; + +const common = [ + 'opacity', + 'zIndex', +] +const rasterLayer = [ + 'mask', + 'rampColors', + 'domain', + 'clampHigh', + 'clampLow', + 'pixelConstant', + 'pixelConstantR', + 'pixelConstantG', + 'pixelConstantB', + 'pixelConstantRGB', + ...common +] +const pointLayer = [ + 'stroke', + 'strokeWidth', + 'strokeOpacity', + 'color', + 'shape', + 'size', + ...common +] +const lineLayer = [ + 'stroke', + 'strokeWidth', + 'strokeOpacity', + 'color', + 'shape', + 'size', + ...common +] +const polygonLayer = [ + 'color', + 'shape', + ...common +] +export type IStyles = 'PointLayer'| 'LineLayer' | 'PolygonLayer' | 'RasterLayer' | 'MaskLayer' | 'TileDebugLayer'; +export const styles = { + 'PointLayer': pointLayer, + 'LineLayer': lineLayer, + 'PolygonLayer': polygonLayer, + + 'RasterLayer': rasterLayer, + + 'MaskLayer': [], + 'TileDebugLayer': [], +} + diff --git a/packages/layers/src/tile/style/utils.ts b/packages/layers/src/tile/style/utils.ts new file mode 100644 index 0000000000..5e60eb37b4 --- /dev/null +++ b/packages/layers/src/tile/style/utils.ts @@ -0,0 +1,134 @@ +import { + ILayer, + IRendererService, + ScaleAttributeType, + IScaleValue + } from '@antv/l7-core'; + + import { generateColorRamp, IColorRamp } from '@antv/l7-utils'; + +export function updateTexture(config: IColorRamp, layers: ILayer[], rendererService: IRendererService) { + const { createTexture2D } = rendererService; + const imageData = generateColorRamp(config) as ImageData; + const texture = createTexture2D({ + data: imageData.data, + width: imageData.width, + height: imageData.height, + flipY: false, + }); + + layers.map(layer => { + layer.updateLayerConfig({ + colorTexture: texture + }); + }) + return texture; +} + +export function updateLayersConfig(layers: ILayer[], key: string, value: any) { + layers.map((layer) => { + if (key === 'mask') { + // Tip: 栅格瓦片生效、设置全局的 mask、瓦片被全局的 mask 影响 + layer.style({ + mask: value, + }); + } else { + layer.updateLayerConfig({ + [key]: value, + }); + } + }); + } + +export function setStyleAttributeField( + layer: ILayer, + parent: ILayer, + style: ScaleAttributeType, + value: IScaleValue | undefined | string | string[], +) { + if (Array.isArray(value)) { + // @ts-ignore + layer[style](...value); + return; + } + if (typeof value === 'string') { + layer[style](value); + return; + } + const defaultValue = getDefaultStyleAttributeField(layer, parent.type, style); + if (!value) { + layer[style](defaultValue); + return layer; + } + const params = parseScaleValue(value, style); + if (params.length === 0) { + layer[style](defaultValue); + } else { + // @ts-ignore + layer[style](...params); + } +} + +export function getDefaultStyleAttributeField(layer: ILayer, type: string, style: string) { + switch (style) { + case 'size': + return 1; + case 'color': + return '#fff'; + case 'shape': + return getLayerShape(type, layer); + default: + return ''; + } +} + +export function getLayerShape(layerType: string, layer: ILayer) { + const layerShape = layer.getAttribute('shape'); + if (layerShape && layerShape.scale?.field) { + if (layerShape.scale?.values === 'text') { + return [layerShape.scale.field, layerShape.scale.values] as string[]; + } + return layerShape.scale.field as string; + } + switch (layerType) { + case 'PolygonLayer': + return 'fill'; + case 'LineLayer': + return 'tileline'; + case 'PointLayer': + return 'circle'; + case 'RasterLayer': + return 'image'; + default: + return ''; + } +} + +export function parseScaleValue(value: IScaleValue | string, type: string) { + if (type === 'shape') { + if (typeof value === 'string') { + return [value]; + } else if (value?.field) { + return [value?.field]; + } else { + return []; + } + } + const { field, values, callback } = value as IScaleValue; + if (field && values && Array.isArray(values)) { + return [field, values]; + } else if (field && callback) { + return [field, callback]; + } else if (field) { + return [field]; + } + return []; +} + +export function setScale(layer: ILayer, parent: ILayer) { + const scaleOptions = parent.tileLayer.scaleField; + const scaleKeys = Object.keys(scaleOptions); + scaleKeys.map((key) => { + layer.scale(key, scaleOptions[key]); + }); +} \ No newline at end of file diff --git a/packages/layers/src/tile/tileFactory/base.ts b/packages/layers/src/tile/tileFactory/base.ts index 051a1a6ec7..decfa61b95 100644 --- a/packages/layers/src/tile/tileFactory/base.ts +++ b/packages/layers/src/tile/tileFactory/base.ts @@ -3,19 +3,14 @@ import { IMapService, IParseDataItem, IRendererService, - IScaleValue, ISubLayerInitOptions, - ScaleAttributeType, } from '@antv/l7-core'; import Source from '@antv/l7-source'; import { osmLonLat2TileXY, Tile, TilesetManager } from '@antv/l7-utils'; - -import { - getLayerShape, - readRasterValue, - registerLayers, -} from '../utils'; -import VectorLayer from './vectorLayer'; +import { setStyleAttributeField, setScale } from '../style/utils'; +import { registerLayers } from '../utils'; +import { readRasterValue } from '../interaction/getRasterData'; +import VectorLayer from './layers/vectorLayer'; import * as turf from '@turf/helpers'; import union from '@turf/union'; @@ -72,6 +67,7 @@ export default class TileFactory implements ITileFactory { } public getFeatureData(tile: Tile, initOptions: ISubLayerInitOptions) { + const { sourceLayer, featureId, transforms = [], layerType, shape } = initOptions; if (!sourceLayer) { return EMPTY_FEATURE_DATA; @@ -144,7 +140,6 @@ export default class TileFactory implements ITileFactory { }); if(layerType) layer.type = layerType; - // Tip: sign tile layer layer.isTileLayer = true; // vector 、raster @@ -158,13 +153,13 @@ export default class TileFactory implements ITileFactory { layer.source(source); // set scale attribute field - this.setStyleAttributeField(layer, 'shape', shape); + setStyleAttributeField(layer, this.parentLayer, 'shape', shape); if(usage !== 'basemap') { // set scale - this.setScale(layer); + setScale(layer, this.parentLayer); - this.setStyleAttributeField(layer, 'color', color); - this.setStyleAttributeField(layer, 'size', size); + setStyleAttributeField(layer, this.parentLayer, 'color', color); + setStyleAttributeField(layer, this.parentLayer, 'size', size); } else { layer.style({ color: basemapColor, @@ -173,7 +168,6 @@ export default class TileFactory implements ITileFactory { } // set mask - const layers = [layer]; if (mask && layer.isVector) { const masklayer = new VectorLayer({layerType: "MaskLayer"}) .source({ @@ -186,12 +180,10 @@ export default class TileFactory implements ITileFactory { } }); - layers.push(masklayer as VectorLayer); + registerLayers(this.parentLayer, [masklayer]); layer.addMaskLayer(masklayer); } - // regist layer - registerLayers(this.parentLayer, layers); this.layers = [layer]; @@ -203,68 +195,6 @@ export default class TileFactory implements ITileFactory { return ''; } - public getDefaultStyleAttributeField(layer: ILayer, type: string) { - switch (type) { - case 'size': - return 1; - case 'color': - return '#fff'; - case 'shape': - return getLayerShape(this.parentLayer.type, layer); - default: - return ''; - } - } - - public setStyleAttributeField( - layer: ILayer, - type: ScaleAttributeType, - value: IScaleValue | undefined | string | string[], - ) { - if (Array.isArray(value)) { - // @ts-ignore - layer[type](...value); - return; - } - if (typeof value === 'string') { - layer[type](value); - return; - } - const defaultValue = this.getDefaultStyleAttributeField(layer, type); - if (!value) { - layer[type](defaultValue); - return layer; - } - const params = this.parseScaleValue(value, type); - if (params.length === 0) { - layer[type](defaultValue); - } else { - // @ts-ignore - layer[type](...params); - } - } - - protected parseScaleValue(value: IScaleValue | string, type: string) { - if (type === 'shape') { - if (typeof value === 'string') { - return [value]; - } else if (value?.field) { - return [value?.field]; - } else { - return []; - } - } - const { field, values, callback } = value as IScaleValue; - if (field && values && Array.isArray(values)) { - return [field, values]; - } else if (field && callback) { - return [field, callback]; - } else if (field) { - return [field]; - } - return []; - } - protected getTile(lng: number, lat: number) { const zoom = this.mapService.getZoom(); const z = Math.ceil(zoom) + this.zoomOffset; @@ -409,14 +339,6 @@ export default class TileFactory implements ITileFactory { this.parentLayer.emit(eventName, e); } - private setScale(layer: ILayer) { - const scaleOptions = this.parentLayer.tileLayer.scaleField; - const scaleKeys = Object.keys(scaleOptions); - scaleKeys.map((key) => { - layer.scale(key, scaleOptions[key]); - }); - } - private getAllFeatures(featureId: number) { const allLayers: ILayer[] = this.parentLayer.tileLayer.children; const features: IParseDataItem[] = []; diff --git a/packages/layers/src/tile/tileFactory/rasterDataLayer.ts b/packages/layers/src/tile/tileFactory/layers/rasterDataLayer.ts similarity index 78% rename from packages/layers/src/tile/tileFactory/rasterDataLayer.ts rename to packages/layers/src/tile/tileFactory/layers/rasterDataLayer.ts index 5f3f0eb282..f3dcae5138 100644 --- a/packages/layers/src/tile/tileFactory/rasterDataLayer.ts +++ b/packages/layers/src/tile/tileFactory/layers/rasterDataLayer.ts @@ -1,7 +1,7 @@ -import BaseLayer from '../../core/BaseLayer'; -import { IRasterLayerStyleOptions } from '../../core/interface'; -import RasterModel from '../../raster/models/rasterTile'; -import RasterRgbModel from '../../raster/models/rasterRgb'; +import BaseLayer from '../../../core/BaseLayer'; +import { IRasterLayerStyleOptions } from '../../../core/interface'; +import RasterModel from '../../../raster/models/rasterTile'; +import RasterRgbModel from '../../../raster/models/rasterRgb'; export default class RasterTiffLayer extends BaseLayer< Partial diff --git a/packages/layers/src/tile/tileTest.ts b/packages/layers/src/tile/tileFactory/layers/tileTest.ts similarity index 73% rename from packages/layers/src/tile/tileTest.ts rename to packages/layers/src/tile/tileFactory/layers/tileTest.ts index c9657f97c4..6abfb5b8c7 100644 --- a/packages/layers/src/tile/tileTest.ts +++ b/packages/layers/src/tile/tileFactory/layers/tileTest.ts @@ -1,6 +1,6 @@ -import BaseLayer from '../core/BaseLayer'; -import { IBaseLayerStyleOptions } from '../core/interface'; -import TileModel from './models/tileModel'; +import BaseLayer from '../../../core/BaseLayer'; +import { IBaseLayerStyleOptions } from '../../../core/interface'; +import TileModel from '../../models/tileModel'; export default class TileDebugLayer extends BaseLayer { public type: string = 'TileDebugLayer'; diff --git a/packages/layers/src/tile/tileFactory/vectorLayer.ts b/packages/layers/src/tile/tileFactory/layers/vectorLayer.ts similarity index 93% rename from packages/layers/src/tile/tileFactory/vectorLayer.ts rename to packages/layers/src/tile/tileFactory/layers/vectorLayer.ts index cf0fede28e..830ab34721 100644 --- a/packages/layers/src/tile/tileFactory/vectorLayer.ts +++ b/packages/layers/src/tile/tileFactory/layers/vectorLayer.ts @@ -12,21 +12,21 @@ import { IShaderModuleService, IStyleAttributeService, } from '@antv/l7-core'; -import BaseLayer from '../../core/BaseLayer'; +import BaseLayer from '../../../core/BaseLayer'; import { ILineLayerStyleOptions, IPointLayerStyleOptions, IPolygonLayerStyleOptions, IMaskLayerStyleOptions, -} from '../../core/interface'; -import lineFillModel from '../../line/models/tile'; -import lineSimpleModel from '../../line/models/simpleTileLine'; +} from '../../../core/interface'; +import lineFillModel from '../../../line/models/tile'; +import lineSimpleModel from '../../../line/models/simpleTileLine'; -import pointTextModel from '../../point/models/tileText'; -import pointFillModel from '../../point/models/tile'; -import polygonFillModel from '../../polygon/models/tile'; +import pointTextModel from '../../../point/models/tileText'; +import pointFillModel from '../../../point/models/tile'; +import polygonFillModel from '../../../polygon/models/tile'; -import maskModel from '../../mask/models/fill'; +import maskModel from '../../../mask/models/fill'; type ILayerStyleOptions = IPolygonLayerStyleOptions & ILineLayerStyleOptions & IPointLayerStyleOptions & IMaskLayerStyleOptions; diff --git a/packages/layers/src/tile/tileFactory/line.ts b/packages/layers/src/tile/tileFactory/line.ts index 8ed68ba8ef..c7614b28e1 100644 --- a/packages/layers/src/tile/tileFactory/line.ts +++ b/packages/layers/src/tile/tileFactory/line.ts @@ -29,9 +29,6 @@ export default class VectorLineTile extends TileFactory { vectorTileLayer, source: source as Source, }); - layer.once('modelLoaded', () => { - tile.layerLoad(); - }) return { layers: [layer], layerIDList: [layer.id], diff --git a/packages/layers/src/tile/tileFactory/mask.ts b/packages/layers/src/tile/tileFactory/mask.ts index 45b452e709..df9f72647b 100644 --- a/packages/layers/src/tile/tileFactory/mask.ts +++ b/packages/layers/src/tile/tileFactory/mask.ts @@ -30,9 +30,6 @@ export default class VectorMaskTile extends TileFactory { source: source as Source, needListen: false }); - layer.once('modelLoaded', () => { - tile.layerLoad(); - }) return { layers: [layer], layerIDList: [layer.id], diff --git a/packages/layers/src/tile/tileFactory/point.ts b/packages/layers/src/tile/tileFactory/point.ts index fd9066975a..ff83463db9 100644 --- a/packages/layers/src/tile/tileFactory/point.ts +++ b/packages/layers/src/tile/tileFactory/point.ts @@ -30,9 +30,6 @@ export default class VectorPointTile extends TileFactory { source: source as Source, needListen: false }); - layer.once('modelLoaded', () => { - tile.layerLoad(); - }) return { layers: [layer], layerIDList: [layer.id], diff --git a/packages/layers/src/tile/tileFactory/polygon.ts b/packages/layers/src/tile/tileFactory/polygon.ts index 389a5763de..7cf5669929 100644 --- a/packages/layers/src/tile/tileFactory/polygon.ts +++ b/packages/layers/src/tile/tileFactory/polygon.ts @@ -29,9 +29,6 @@ export default class VectorPolygonTile extends TileFactory { vectorTileLayer, source: source as Source, }); - layer.once('modelLoaded', () => { - tile.layerLoad(); - }) return { layers: [layer], layerIDList: [layer.id], diff --git a/packages/layers/src/tile/tileFactory/raster.ts b/packages/layers/src/tile/tileFactory/raster.ts index 413dcb7bfa..53304d0a43 100644 --- a/packages/layers/src/tile/tileFactory/raster.ts +++ b/packages/layers/src/tile/tileFactory/raster.ts @@ -26,9 +26,6 @@ export default class RasterTile extends TileFactory { initOptions, source, }); - layer.once('modelLoaded', () => { - tile.layerLoad(); - }) return { layers: [layer], layerIDList: [layer.id], diff --git a/packages/layers/src/tile/tileFactory/rasterData.ts b/packages/layers/src/tile/tileFactory/rasterData.ts index 24ec56411c..7f18f9040a 100644 --- a/packages/layers/src/tile/tileFactory/rasterData.ts +++ b/packages/layers/src/tile/tileFactory/rasterData.ts @@ -1,9 +1,8 @@ import { ILayer, ISubLayerInitOptions } from '@antv/l7-core'; import { Tile } from '@antv/l7-utils'; import { ITileFactoryOptions } from '../interface'; -import { registerLayers } from '../utils'; import TileFactory from './base'; -import RasterDataLayer from './rasterDataLayer'; +import RasterDataLayer from './layers/rasterDataLayer'; export default class RasterTiffTile extends TileFactory { public parentLayer: ILayer; @@ -54,10 +53,6 @@ export default class RasterTiffTile extends TileFactory { }); this.emitEvent([layer], false); - registerLayers(this.parentLayer, [layer]); - layer.once('modelLoaded', () => { - tile.layerLoad(); - }) return { layers: [layer], layerIDList: [layer.id], diff --git a/packages/layers/src/tile/tileFactory/test.ts b/packages/layers/src/tile/tileFactory/test.ts index 5c2cd82fa4..c9ebb690c0 100644 --- a/packages/layers/src/tile/tileFactory/test.ts +++ b/packages/layers/src/tile/tileFactory/test.ts @@ -2,10 +2,7 @@ import { ILayer, ISubLayerInitOptions } from '@antv/l7-core'; import { Tile } from '@antv/l7-utils'; import { ITileFactoryOptions } from '../interface'; import TileFactory from './base'; -import VectorLayer from './vectorLayer'; -import { - registerLayers, -} from '../utils'; +import VectorLayer from './layers/vectorLayer'; export default class TestTile extends TileFactory { public parentLayer: ILayer; @@ -71,13 +68,6 @@ export default class TestTile extends TileFactory { text.isTileLayer = true; line.isTileLayer = true; - registerLayers(this.parentLayer, [line, text]); - text.once('modelLoaded', () => { - tile.layerLoad(); - }) - line.once('modelLoaded', () => { - tile.layerLoad(); - }) return { layers: [line, text], layerIDList: [line.id, text.id], diff --git a/packages/layers/src/tile/tileLayer/MapTileLayer.ts b/packages/layers/src/tile/tileLayer/MapTileLayer.ts new file mode 100644 index 0000000000..a92b813875 --- /dev/null +++ b/packages/layers/src/tile/tileLayer/MapTileLayer.ts @@ -0,0 +1,35 @@ +import { + IBaseTileLayer, + ITileLayerOPtions, + IBaseTileLayerManager, +} from '@antv/l7-core'; +import { BaseMapTileLayerManager } from '../manager/mapLayerManager'; + +import { Base } from './base'; + +export class MapTileLayer extends Base implements IBaseTileLayer { + public tileLayerManager: IBaseTileLayerManager; + constructor({ + parent, + rendererService, + mapService, + layerService, + }: ITileLayerOPtions) { + super(); + const parentSource = parent.getSource(); + const { sourceLayer } = + parentSource?.data?.tilesetOptions || {}; + this.sourceLayer = sourceLayer; + this.parent = parent; + this.mapService = mapService; + this.layerService = layerService; + + this.tileLayerManager = new BaseMapTileLayerManager( + parent, + mapService, + rendererService, + ); + + this.initTileSetManager(); + } +} diff --git a/packages/layers/src/tile/tileLayer/TileLayer.ts b/packages/layers/src/tile/tileLayer/TileLayer.ts new file mode 100644 index 0000000000..220cd16b71 --- /dev/null +++ b/packages/layers/src/tile/tileLayer/TileLayer.ts @@ -0,0 +1,168 @@ +import { + IInteractionTarget, + ILayer, + ITileLayer, + ITileLayerManager, + ITileLayerOPtions, +} from '@antv/l7-core'; +import { TileLayerManager } from '../manager/layerManager'; +import { Base } from './base'; +import { setSelect, setHighlight, setPickState, clearPickState } from '../interaction/utils'; + +export class TileLayer extends Base implements ITileLayer { + public get children() { + return this.tileLayerManager.children; + } + public tileLayerManager: ITileLayerManager; + + private pickColors: { + select: any; + active: any; + } = { + select: null, + active: null, + }; + + constructor({ + parent, + rendererService, + mapService, + layerService, + pickingService, + transforms + }: ITileLayerOPtions) { + super(); + const parentSource = parent.getSource(); + const { sourceLayer } = + parentSource?.data?.tilesetOptions || {}; + this.sourceLayer = sourceLayer; + this.parent = parent; + this.mapService = mapService; + this.layerService = layerService; + + this.tileLayerManager = new TileLayerManager( + parent, + mapService, + rendererService, + pickingService, + transforms + ); + + this.initTileSetManager(); + this.bindSubLayerEvent(); + this.bindSubLayerPick(); + + this.scaleField = this.parent.getScaleOptions(); + } + + public clearPick(type: string) { + // Tip: 瓦片只有在 mousemove 的时候需要设置清除 + if (type === 'mousemove') { + this.tileLayerManager.tilePickService.clearPick(); + } + } + + /** + * 清除 select 的选中状态 + */ + public clearPickState() { + clearPickState(this.children) + } + + /** + * 瓦片图层独立的拾取逻辑 + * @param target + * @returns + */ + public pickLayers(target: IInteractionTarget) { + return this.tileLayerManager.pickLayers(target); + } + + public setPickState(layers: ILayer[]) { + setPickState(layers, this.pickColors); + } + + private bindSubLayerPick() { + this.tileLayerManager.tilePickService.on('pick', (e) => { + // @ts-ignore + const [r, g, b] = e.pickedColors; + + if (e.type === 'click') { + const restLayers = this.children + .filter( + (child) => child.inited && child.isVisible() && child.isVector, + ) + .filter((child) => child !== e.layer); + + const renderList = this.layerService.getRenderList(); + const color = setSelect(restLayers, [r, g, b], renderList) + this.pickColors.select = color; + } else { + setHighlight(this.children, [r, g, b]); + this.pickColors.active = [r, g, b]; + } + }); + + this.tileLayerManager.tilePickService.on('unpick', () => { + this.pickColors.active = null; + }); + } + + protected bindSubLayerEvent() { + /** + * layer.on('click', (ev) => {}); // 鼠标左键点击图层事件 + * layer.on('mouseenter', (ev) => {}); // 鼠标进入图层要素 + * layer.on('mousemove', (ev) => {}); // 鼠标在图层上移动时触发 + * layer.on('mouseout', (ev) => {}); // 鼠标移出图层要素时触发 + * layer.on('mouseup', (ev) => {}); // 鼠标在图层上单击抬起时触发 + * layer.on('mousedown', (ev) => {}); // 鼠标在图层上单击按下时触发 + * layer.on('contextmenu', (ev) => {}); // 图层要素点击右键菜单 + * + * 鼠标在图层外的事件 + * layer.on('unclick', (ev) => {}); // 图层外点击 + * layer.on('unmousemove', (ev) => {}); // 图层外移动 + * layer.on('unmouseup', (ev) => {}); // 图层外鼠标抬起 + * layer.on('unmousedown', (ev) => {}); // 图层外单击按下时触发 + * layer.on('uncontextmenu', (ev) => {}); // 图层外点击右键 + * layer.on('unpick', (ev) => {}); // 图层外的操作的所有事件 + */ + this.parent.on('subLayerClick', (e) => { + this.parent.emit('click', { ...e }); + }); + this.parent.on('subLayerMouseMove', (e) => + this.parent.emit('mousemove', { ...e }), + ); + this.parent.on('subLayerMouseUp', (e) => + this.parent.emit('mouseup', { ...e }), + ); + this.parent.on('subLayerMouseEnter', (e) => + this.parent.emit('mouseenter', { ...e }), + ); + this.parent.on('subLayerMouseOut', (e) => + this.parent.emit('mouseout', { ...e }), + ); + this.parent.on('subLayerMouseDown', (e) => + this.parent.emit('mousedown', { ...e }), + ); + this.parent.on('subLayerContextmenu', (e) => + this.parent.emit('contextmenu', { ...e }), + ); + + // vector layer 图层外事件 + this.parent.on('subLayerUnClick', (e) => + this.parent.emit('unclick', { ...e }), + ); + this.parent.on('subLayerUnMouseMove', (e) => + this.parent.emit('unmousemove', { ...e }), + ); + this.parent.on('subLayerUnMouseUp', (e) => + this.parent.emit('unmouseup', { ...e }), + ); + this.parent.on('subLayerUnMouseDown', (e) => + this.parent.emit('unmousedown', { ...e }), + ); + this.parent.on('subLayerUnContextmenu', (e) => + this.parent.emit('uncontextmenu', { ...e }), + ); + } +} diff --git a/packages/layers/src/tile/tileLayer/base.ts b/packages/layers/src/tile/tileLayer/base.ts new file mode 100644 index 0000000000..1efe89afd4 --- /dev/null +++ b/packages/layers/src/tile/tileLayer/base.ts @@ -0,0 +1,183 @@ +import { + ILayer, + IMapService, + ILayerService, + ISource, + } from '@antv/l7-core'; +import { Tile, TilesetManager } from '@antv/l7-utils'; +import { debounce } from 'lodash'; +import { updateTileVisible } from '../utils'; + +export class Base { + public tileLayerManager: any; + public get children() { + return this.tileLayerManager.children; + } + public sourceLayer: string; + public parent: ILayer; + public initedTileset: boolean = false; // 瓦片是否加载成功 + + public tilesetManager: TilesetManager | undefined; // 瓦片数据管理器 + public scaleField: any; + + protected mapService: IMapService; + protected layerService: ILayerService; + + protected lastViewStates: { + zoom: number; + latLonBounds: [number, number, number, number]; + }; + + protected mapchange() { + const { latLonBounds, zoom } = this.getCurrentView(); + + if (this.mapService.version === 'GAODE1.x') { + const { visible } = this.parent.getLayerConfig(); + if (zoom < 3 && visible) { + this.parent.updateLayerConfig({ visible: false }); + this.layerService.reRender(); + } else if (zoom >= 3 && !visible) { + this.parent.updateLayerConfig({ visible: true }); + this.layerService.reRender(); + } + } + + if ( + this.lastViewStates && + this.lastViewStates.zoom === zoom && + this.lastViewStates.latLonBounds.toString() === latLonBounds.toString() + ) { + return; + } + this.lastViewStates = { zoom, latLonBounds }; + + this.tilesetManager?.throttleUpdate(zoom, latLonBounds); + } + + protected getCurrentView() { + const bounds = this.mapService.getBounds(); + const latLonBounds: [number, number, number, number] = [ + bounds[0][0], + bounds[0][1], + bounds[1][0], + bounds[1][1], + ]; + const zoom = this.mapService.getZoom(); + + return { latLonBounds, zoom }; + } + + protected initTileSetManager() { + const source: ISource = this.parent.getSource(); + this.tilesetManager = source.tileset as TilesetManager; + + if (!this.initedTileset) { + this.bindTilesetEvent(); + this.initedTileset = true; + } + + const { latLonBounds, zoom } = this.getCurrentView(); + this.tilesetManager?.update(zoom, latLonBounds); + } + + private bindTilesetEvent() { + if (!this.tilesetManager) { + return; + } + // 瓦片数据加载成功 + // eslint-disable-next-line @typescript-eslint/no-unused-vars + this.tilesetManager.on('tile-loaded', (tile: Tile) => { + // 将事件抛出,图层上可以监听使用 + }); + + // 瓦片数据从缓存删除或被执行重新加载 + this.tilesetManager.on('tile-unload', (tile: Tile) => { + // 将事件抛出,图层上可以监听使用 + this.tileUnLoad(tile); + }); + + // 瓦片数据加载失败 + // eslint-disable-next-line @typescript-eslint/no-unused-vars + this.tilesetManager.on('tile-error', (error, tile: Tile) => { + // 将事件抛出,图层上可以监听使用 + this.tileError(error); + }); + + // 瓦片显隐状态更新 + this.tilesetManager.on('tile-update', () => { + this.tileUpdate(); + }); + + // 地图视野发生改变 + this.mapService.on('zoomend', () => this.viewchange()); + this.mapService.on('moveend', () => this.viewchange()); + } + + public render() { + this.tileLayerManager.render(); + } + + // 防抖操作 + viewchange = debounce(this.mapchange, 200) + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + public tileLoaded(tile: Tile) { + // + } + + public tileError(error: Error) { + console.warn('error:', error); + } + + public destroy() { + this.tilesetManager?.destroy(); + this.tileLayerManager.destroy(); + } + + public tileUnLoad(tile: Tile) { + this.tileLayerManager.removeChildren(tile.layerIDList, false); + } + + public tileUpdate() { + if (!this.tilesetManager) { + return; + } + this.tilesetManager.tiles + .filter((tile: Tile) => tile.isLoaded) + .map((tile: Tile) => { + if (tile.data?.layers && this.sourceLayer) { + // vector + const vectorTileLayer = tile.data.layers[this.sourceLayer]; + const features = vectorTileLayer?.features; + if (!(Array.isArray(features) && features.length > 0)) { + return; + } + } + if (!tile.parentLayerIDList.includes(this.parent.id)) { + const { layers, layerIDList } = this.tileLayerManager.createTile( + tile, + ); + tile.parentLayerIDList.push(this.parent.id); + tile.layerIDList.push(...layerIDList); + + this.tileLayerManager.addChildren(layers); + this.setPickState(layers) + } else { + if (!tile.isVisibleChange) { + return; + } + const layers = this.tileLayerManager.getChildren(tile.layerIDList); + updateTileVisible(tile, layers, this.layerService); + this.setPickState(layers) + } + }); + + if (this.tilesetManager.isLoaded) { + // 将事件抛出,图层上可以使用瓦片 + this.parent.emit('tiles-loaded', this.tilesetManager.currentTiles); + } + } + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + public setPickState(layers: ILayer[]) {} +} \ No newline at end of file diff --git a/packages/layers/src/tile/tileLayer/baseMapTileLayer.ts b/packages/layers/src/tile/tileLayer/baseMapTileLayer.ts deleted file mode 100644 index bd15cf82cb..0000000000 --- a/packages/layers/src/tile/tileLayer/baseMapTileLayer.ts +++ /dev/null @@ -1,183 +0,0 @@ -import { - ILayer, - ILayerService, - IMapService, - ISource, - IBaseTileLayer, - IBaseTileLayerManager, - ITileLayerOPtions, -} from '@antv/l7-core'; -import { Tile, TilesetManager } from '@antv/l7-utils'; -import { BaseMapTileLayerManager } from '../manager/baseMapTileLayerManager'; -import { debounce } from 'lodash'; - -export default class BaseTileLayer implements IBaseTileLayer { - public get children() { - return this.tileLayerManager.children; - } - public type: string = 'baseTile'; - public sourceLayer: string; - public parent: ILayer; - // 瓦片是否加载成功 - public initedTileset: boolean = false; - // 瓦片数据管理器 - public tilesetManager: TilesetManager | undefined; - public tileLayerManager: IBaseTileLayerManager; - public scaleField: any; - - private lastViewStates: { - zoom: number; - latLonBounds: [number, number, number, number]; - }; - - protected mapService: IMapService; - protected layerService: ILayerService; - - constructor({ - parent, - rendererService, - mapService, - layerService, - }: ITileLayerOPtions) { - const parentSource = parent.getSource(); - const { sourceLayer } = - parentSource?.data?.tilesetOptions || {}; - this.sourceLayer = sourceLayer; - this.parent = parent; - this.mapService = mapService; - this.layerService = layerService; - - this.tileLayerManager = new BaseMapTileLayerManager( - parent, - mapService, - rendererService, - ); - - this.initTileSetManager(); - } - - /** - * 渲染瓦片的图层 - */ - public render() { - if (this.tileLayerManager) { - this.tileLayerManager.render(); - } - } - - // eslint-disable-next-line @typescript-eslint/no-unused-vars - public tileLoaded(tile: Tile) { - // - } - - public tileError(error: Error) { - console.warn('error:', error); - } - - public tileUnLoad(tile: Tile) { - this.tileLayerManager.removeChilds(tile.layerIDList, false); - } - - public tileUpdate() { - // Base Function - if (!this.tilesetManager) { - return; - } - - if (this.tilesetManager.isLoaded) { - // 将事件抛出,图层上可以使用瓦片 - this.parent.emit('tiles-loaded', this.tilesetManager.currentTiles); - } - } - - private initTileSetManager() { - const source: ISource = this.parent.getSource(); - this.tilesetManager = source.tileset as TilesetManager; - - if (!this.initedTileset) { - this.bindTilesetEvent(); - this.initedTileset = true; - } - - const { latLonBounds, zoom } = this.getCurrentView(); - this.tilesetManager?.update(zoom, latLonBounds); - } - - private mapchange() { - const { latLonBounds, zoom } = this.getCurrentView(); - - if (this.mapService.version === 'GAODE1.x') { - const { visible } = this.parent.getLayerConfig(); - if (zoom < 3 && visible) { - this.parent.updateLayerConfig({ visible: false }); - this.layerService.reRender(); - } else if (zoom >= 3 && !visible) { - this.parent.updateLayerConfig({ visible: true }); - this.layerService.reRender(); - } - } - - if ( - this.lastViewStates && - this.lastViewStates.zoom === zoom && - this.lastViewStates.latLonBounds.toString() === latLonBounds.toString() - ) { - return; - } - this.lastViewStates = { zoom, latLonBounds }; - - this.tilesetManager?.throttleUpdate(zoom, latLonBounds); - } - - private bindTilesetEvent() { - if (!this.tilesetManager) { - return; - } - // 瓦片数据加载成功 - // eslint-disable-next-line @typescript-eslint/no-unused-vars - this.tilesetManager.on('tile-loaded', (tile: Tile) => { - // 将事件抛出,图层上可以监听使用 - }); - - // 瓦片数据从缓存删除或被执行重新加载 - this.tilesetManager.on('tile-unload', (tile: Tile) => { - // 将事件抛出,图层上可以监听使用 - this.tileUnLoad(tile); - }); - - // 瓦片数据加载失败 - this.tilesetManager.on('tile-error', (error, tile: Tile) => { - // 将事件抛出,图层上可以监听使用 - this.tileError(error); - }); - - // 瓦片显隐状态更新 - this.tilesetManager.on('tile-update', () => { - this.tileUpdate(); - }); - - // 地图视野发生改变 - this.mapService.on('zoomend', () => this.viewchange()); - this.mapService.on('moveend', () => this.viewchange()); - } - - // 防抖操作 - viewchange = debounce(this.mapchange, 200) - - private getCurrentView() { - const bounds = this.mapService.getBounds(); - const latLonBounds: [number, number, number, number] = [ - bounds[0][0], - bounds[0][1], - bounds[1][0], - bounds[1][1], - ]; - const zoom = this.mapService.getZoom(); - - return { latLonBounds, zoom }; - } - - public destroy() { - this.tilesetManager?.destroy(); - } -} diff --git a/packages/layers/src/tile/tileLayer/baseTileLayer.ts b/packages/layers/src/tile/tileLayer/baseTileLayer.ts deleted file mode 100644 index eb751e9e40..0000000000 --- a/packages/layers/src/tile/tileLayer/baseTileLayer.ts +++ /dev/null @@ -1,383 +0,0 @@ -import { - IInteractionTarget, - ILayer, - ILayerService, - IMapService, - ISource, - ITileLayer, - ITileLayerManager, - ITileLayerOPtions, -} from '@antv/l7-core'; -import { decodePickingColor, Tile, TilesetManager } from '@antv/l7-utils'; -import { TileLayerManager } from '../manager/tileLayerManager'; -import { debounce } from 'lodash'; - -export default class BaseTileLayer implements ITileLayer { - public get children() { - return this.tileLayerManager.children; - } - public type: string = 'baseTile'; - public sourceLayer: string; - public parent: ILayer; - // 瓦片是否加载成功 - public initedTileset: boolean = false; - // 瓦片数据管理器 - public tilesetManager: TilesetManager | undefined; - public tileLayerManager: ITileLayerManager; - public scaleField: any; - - private lastViewStates: { - zoom: number; - latLonBounds: [number, number, number, number]; - }; - - protected mapService: IMapService; - protected layerService: ILayerService; - private pickColors: { - select: any; - active: any; - } = { - select: null, - active: null, - }; - - constructor({ - parent, - rendererService, - mapService, - layerService, - pickingService, - transforms - }: ITileLayerOPtions) { - const parentSource = parent.getSource(); - const { sourceLayer } = - parentSource?.data?.tilesetOptions || {}; - this.sourceLayer = sourceLayer; - this.parent = parent; - this.mapService = mapService; - this.layerService = layerService; - - this.tileLayerManager = new TileLayerManager( - parent, - mapService, - rendererService, - pickingService, - transforms - ); - - this.initTileSetManager(); - this.bindSubLayerEvent(); - this.bindSubLayerPick(); - - this.scaleField = this.parent.getScaleOptions(); - } - - /** - * 渲染瓦片的图层 - */ - public render() { - if (this.tileLayerManager) { - this.tileLayerManager.render(); - } - } - - public clearPick(type: string) { - if (type === 'mousemove') { - this.tileLayerManager.tilePickManager.clearPick(); - } - } - - /** - * 清除 select 的选中状态 - */ - public clearPickState() { - this.children - .filter((child) => child.inited && child.isVisible()) - .filter((child) => child.getCurrentSelectedId() !== null) - .map((child) => { - this.selectFeature(child, new Uint8Array([0, 0, 0, 0])); - child.setCurrentSelectedId(null); - }); - } - - /** - * 瓦片图层独立的拾取逻辑 - * @param target - * @returns - */ - public pickLayers(target: IInteractionTarget) { - return this.tileLayerManager.pickLayers(target); - } - - // eslint-disable-next-line @typescript-eslint/no-unused-vars - public tileLoaded(tile: Tile) { - // - } - - public tileError(error: Error) { - console.warn('error:', error); - } - - public tileUnLoad(tile: Tile) { - this.tileLayerManager.removeChilds(tile.layerIDList, false); - } - - public tileUpdate() { - // Base Function - if (!this.tilesetManager) { - return; - } - - if (this.tilesetManager.isLoaded) { - // 将事件抛出,图层上可以使用瓦片 - this.parent.emit('tiles-loaded', this.tilesetManager.currentTiles); - } - } - - protected setPickState(layers: ILayer[]) { - if (this.pickColors.select) { - const selectedId = decodePickingColor(this.pickColors.select); - layers.map((layer) => { - this.selectFeature(layer, this.pickColors.select); - layer.setCurrentSelectedId(selectedId); - }); - } - - if (this.pickColors.active) { - const selectedId = decodePickingColor(this.pickColors.active); - layers - .filter((layer) => layer.inited && layer.isVisible()) - .map((layer) => { - layer.hooks.beforeHighlight.call(this.pickColors.active); - layer.setCurrentPickId(selectedId); - }); - } - } - - private bindSubLayerPick() { - this.tileLayerManager.tilePickManager.on('pick', (e) => { - // @ts-ignore - const [r, g, b] = e.pickedColors; - - if (e.type === 'click') { - const restLayers = this.children - .filter( - (child) => child.inited && child.isVisible() && child.isVector, - ) - .filter((child) => child !== e.layer); - this.setSelect(restLayers, [r, g, b]); - } else { - this.setHighlight([r, g, b]); - } - }); - - this.tileLayerManager.tilePickManager.on('unpick', () => { - this.pickColors.active = null; - }); - } - - private setHighlight(pickedColors: any) { - const pickId = decodePickingColor(pickedColors); - this.pickColors.active = pickedColors; - this.children - .filter((child) => child.inited && child.isVisible() && child.isVector) - // Tip: 使用 vectorLayer 上的 pickID 优化高亮操作(过滤重复操作) - // @ts-ignore - .filter((child) => child.getPickID() !== pickId) - .map((child) => { - // @ts-ignore - child.setPickID(pickId); - child.hooks.beforeHighlight.call(pickedColors); - }); - } - - private setSelect(layers: ILayer[], pickedColors: any) { - const selectedId = decodePickingColor(pickedColors); - layers.map((layer) => { - if ( - layer.getCurrentSelectedId() === null || - selectedId !== layer.getCurrentSelectedId() - ) { - this.selectFeature(layer, pickedColors); - layer.setCurrentSelectedId(selectedId); - this.pickColors.select = pickedColors; - } else { - this.selectFeature(layer, new Uint8Array([0, 0, 0, 0])); // toggle select - layer.setCurrentSelectedId(null); - this.pickColors.select = null; - } - }); - // unselect normal layer - const renderList = this.layerService.getRenderList(); - renderList - .filter( - (layer) => - layer.inited && - !layer.isVector && - layer.isVisible() && - layer.needPick('click'), - ) - .filter((layer) => layer.getCurrentSelectedId() !== null) - .map((layer) => { - this.selectFeature(layer, new Uint8Array([0, 0, 0, 0])); - layer.setCurrentSelectedId(null); - }); - } - - private selectFeature(layer: ILayer, pickedColors: Uint8Array | undefined) { - // @ts-ignore - const [r, g, b] = pickedColors; - layer.hooks.beforeSelect.call([r, g, b]); - } - - private bindSubLayerEvent() { - /** - * layer.on('click', (ev) => {}); // 鼠标左键点击图层事件 - * layer.on('mouseenter', (ev) => {}); // 鼠标进入图层要素 - * layer.on('mousemove', (ev) => {}); // 鼠标在图层上移动时触发 - * layer.on('mouseout', (ev) => {}); // 鼠标移出图层要素时触发 - * layer.on('mouseup', (ev) => {}); // 鼠标在图层上单击抬起时触发 - * layer.on('mousedown', (ev) => {}); // 鼠标在图层上单击按下时触发 - * layer.on('contextmenu', (ev) => {}); // 图层要素点击右键菜单 - * - * 鼠标在图层外的事件 - * layer.on('unclick', (ev) => {}); // 图层外点击 - * layer.on('unmousemove', (ev) => {}); // 图层外移动 - * layer.on('unmouseup', (ev) => {}); // 图层外鼠标抬起 - * layer.on('unmousedown', (ev) => {}); // 图层外单击按下时触发 - * layer.on('uncontextmenu', (ev) => {}); // 图层外点击右键 - * layer.on('unpick', (ev) => {}); // 图层外的操作的所有事件 - */ - this.parent.on('subLayerClick', (e) => { - this.parent.emit('click', { ...e }); - }); - this.parent.on('subLayerMouseMove', (e) => - this.parent.emit('mousemove', { ...e }), - ); - this.parent.on('subLayerMouseUp', (e) => - this.parent.emit('mouseup', { ...e }), - ); - this.parent.on('subLayerMouseEnter', (e) => - this.parent.emit('mouseenter', { ...e }), - ); - this.parent.on('subLayerMouseOut', (e) => - this.parent.emit('mouseout', { ...e }), - ); - this.parent.on('subLayerMouseDown', (e) => - this.parent.emit('mousedown', { ...e }), - ); - this.parent.on('subLayerContextmenu', (e) => - this.parent.emit('contextmenu', { ...e }), - ); - - // vector layer 图层外事件 - this.parent.on('subLayerUnClick', (e) => - this.parent.emit('unclick', { ...e }), - ); - this.parent.on('subLayerUnMouseMove', (e) => - this.parent.emit('unmousemove', { ...e }), - ); - this.parent.on('subLayerUnMouseUp', (e) => - this.parent.emit('unmouseup', { ...e }), - ); - this.parent.on('subLayerUnMouseDown', (e) => - this.parent.emit('unmousedown', { ...e }), - ); - this.parent.on('subLayerUnContextmenu', (e) => - this.parent.emit('uncontextmenu', { ...e }), - ); - } - - private initTileSetManager() { - const source: ISource = this.parent.getSource(); - this.tilesetManager = source.tileset as TilesetManager; - - if (!this.initedTileset) { - this.bindTilesetEvent(); - this.initedTileset = true; - } - - const { latLonBounds, zoom } = this.getCurrentView(); - this.tilesetManager?.update(zoom, latLonBounds); - } - - private mapchange() { - const { latLonBounds, zoom } = this.getCurrentView(); - - if (this.mapService.version === 'GAODE1.x') { - const { visible } = this.parent.getLayerConfig(); - if (zoom < 3 && visible) { - this.parent.updateLayerConfig({ visible: false }); - this.layerService.reRender(); - } else if (zoom >= 3 && !visible) { - this.parent.updateLayerConfig({ visible: true }); - this.layerService.reRender(); - } - } - - if ( - this.lastViewStates && - this.lastViewStates.zoom === zoom && - this.lastViewStates.latLonBounds.toString() === latLonBounds.toString() - ) { - return; - } - this.lastViewStates = { zoom, latLonBounds }; - - this.tilesetManager?.throttleUpdate(zoom, latLonBounds); - } - - private bindTilesetEvent() { - if (!this.tilesetManager) { - return; - } - // 瓦片数据加载成功 - // eslint-disable-next-line @typescript-eslint/no-unused-vars - this.tilesetManager.on('tile-loaded', (tile: Tile) => { - // 将事件抛出,图层上可以监听使用 - }); - - // 瓦片数据从缓存删除或被执行重新加载 - this.tilesetManager.on('tile-unload', (tile: Tile) => { - // 将事件抛出,图层上可以监听使用 - this.tileUnLoad(tile); - }); - - // 瓦片数据加载失败 - // eslint-disable-next-line @typescript-eslint/no-unused-vars - this.tilesetManager.on('tile-error', (error, tile: Tile) => { - // 将事件抛出,图层上可以监听使用 - this.tileError(error); - }); - - // 瓦片显隐状态更新 - this.tilesetManager.on('tile-update', () => { - this.tileUpdate(); - }); - - // 地图视野发生改变 - this.mapService.on('zoomend', () => this.viewchange()); - this.mapService.on('moveend', () => this.viewchange()); - } - - // 防抖操作 - viewchange = debounce(this.mapchange, 200) - - private getCurrentView() { - const bounds = this.mapService.getBounds(); - const latLonBounds: [number, number, number, number] = [ - bounds[0][0], - bounds[0][1], - bounds[1][0], - bounds[1][1], - ]; - const zoom = this.mapService.getZoom(); - - return { latLonBounds, zoom }; - } - - public destroy() { - this.tilesetManager?.destroy(); - this.tileLayerManager.destroy(); - } -} diff --git a/packages/layers/src/tile/tmsMapTileLayer.ts b/packages/layers/src/tile/tmsMapTileLayer.ts deleted file mode 100644 index 07bcd6b858..0000000000 --- a/packages/layers/src/tile/tmsMapTileLayer.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { Tile } from '@antv/l7-utils'; -import BaseTileLayer from './tileLayer/baseMapTileLayer'; -import { updateTileVisible } from './utils'; - -export class TMSBaseMapTileLayer extends BaseTileLayer { - public type: string = 'BaseMapTMS'; - public tileUnLoad(tile: Tile) { - this.tileLayerManager.removeChilds(tile.layerIDList, false); - } - public tileUpdate() { - if (!this.tilesetManager) { - return; - } - this.tilesetManager.tiles - .filter((tile: Tile) => tile.isLoaded) - .map((tile: Tile) => { - if (tile.data?.layers && this.sourceLayer) { - // vector - const vectorTileLayer = tile.data.layers[this.sourceLayer]; - const features = vectorTileLayer?.features; - if (!(Array.isArray(features) && features.length > 0)) { - return; - } - } - if (!tile.parentLayerIDList.includes(this.parent.id)) { - const { layers, layerIDList } = this.tileLayerManager.createTile( - tile, - ); - tile.parentLayerIDList.push(this.parent.id); - tile.layerIDList.push(...layerIDList); - - this.tileLayerManager.addChilds(layers); - } else { - if (!tile.isVisibleChange) { - return; - } - const layers = this.tileLayerManager.getChilds(tile.layerIDList); - updateTileVisible(tile, layers, this.layerService); - } - }); - - if (this.tilesetManager.isLoaded) { - // 将事件抛出,图层上可以使用瓦片 - this.parent.emit('tiles-loaded', this.tilesetManager.currentTiles); - } - } -} diff --git a/packages/layers/src/tile/tmsTileLayer.ts b/packages/layers/src/tile/tmsTileLayer.ts deleted file mode 100644 index 2c174ea50c..0000000000 --- a/packages/layers/src/tile/tmsTileLayer.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { Tile } from '@antv/l7-utils'; -import BaseTileLayer from './tileLayer/baseTileLayer'; -import { updateTileVisible } from './utils'; - -export class TMSTileLayer extends BaseTileLayer { - public type: string = 'TMS'; - public tileUnLoad(tile: Tile) { - this.tileLayerManager.removeChilds(tile.layerIDList, false); - } - public tileUpdate() { - if (!this.tilesetManager) { - return; - } - this.tilesetManager.tiles - .filter((tile: Tile) => tile.isLoaded) - .map((tile: Tile) => { - if (tile.data?.layers && this.sourceLayer) { - // vector - const vectorTileLayer = tile.data.layers[this.sourceLayer]; - const features = vectorTileLayer?.features; - if (!(Array.isArray(features) && features.length > 0)) { - return; - } - } - if (!tile.parentLayerIDList.includes(this.parent.id)) { - const { layers, layerIDList } = this.tileLayerManager.createTile( - tile, - ); - tile.parentLayerIDList.push(this.parent.id); - tile.layerIDList.push(...layerIDList); - - this.tileLayerManager.addChilds(layers); - this.setPickState(layers); - } else { - if (!tile.isVisibleChange) { - return; - } - const layers = this.tileLayerManager.getChilds(tile.layerIDList); - updateTileVisible(tile, layers, this.layerService); - this.setPickState(layers); - } - }); - - if (this.tilesetManager.isLoaded) { - // 将事件抛出,图层上可以使用瓦片 - this.parent.emit('tiles-loaded', this.tilesetManager.currentTiles); - } - } -} diff --git a/packages/layers/src/tile/utils.ts b/packages/layers/src/tile/utils.ts index 0e5e872415..cdd4e92fc1 100644 --- a/packages/layers/src/tile/utils.ts +++ b/packages/layers/src/tile/utils.ts @@ -1,12 +1,7 @@ -import { - createLayerContainer, - ILayer, - IMapService, - IRendererService, - ILayerService, -} from '@antv/l7-core'; +import { createLayerContainer, ILayer, ILayerService } from '@antv/l7-core'; import { DOM, Tile } from '@antv/l7-utils'; import { Container } from 'inversify'; +import { updateLayersConfig } from './style/utils'; export const tileVectorParser = ['mvt', 'geojsonvt', 'testTile']; @@ -72,78 +67,15 @@ export function getContainerSize(container: HTMLCanvasElement | HTMLElement) { } } -export function readRasterValue( - tile: Tile, - mapService: IMapService, - x: number, - y: number, -) { - const bbox = tile?.bboxPolygon?.bbox || [0, 0, 10, -10]; - - const [minLng = 0, minLat = 0, maxLng = 10, maxLat = -10] = bbox; - - const tileXY = mapService.lngLatToContainer([minLng, minLat]); - const tileMaxXY = mapService.lngLatToContainer([maxLng, maxLat]); - - const tilePixelWidth = tileMaxXY.x - tileXY.x; - const tilePixelHeight = tileXY.y - tileMaxXY.y; - const pos = [ - (x - tileXY.x) / tilePixelWidth, // x - (y - tileMaxXY.y) / tilePixelHeight, // y - ]; - - const tileWidth = tile?.data?.width || 1; - const tileHeight = tile?.data?.height || 1; - - const indexX = Math.floor(pos[0] * tileWidth); - const indexY = Math.floor(pos[1] * tileHeight); - const index = Math.max(0, indexY - 1) * tileWidth + indexX; - - const data = tile?.data?.data[index]; - - return data; -} - -export function readPixel( - x: number, - y: number, - rendererService: IRendererService, -) { - const { readPixels, getContainer } = rendererService; - const xInDevicePixel = x * DOM.DPR; - const yInDevicePixel = y * DOM.DPR; - let { width, height } = getContainerSize( - getContainer() as HTMLCanvasElement | HTMLElement, - ); - width *= DOM.DPR; - height *= DOM.DPR; - if ( - xInDevicePixel > width - 1 * DOM.DPR || - xInDevicePixel < 0 || - yInDevicePixel > height - 1 * DOM.DPR || - yInDevicePixel < 0 - ) { - return false; - } - - const pickedColors = readPixels({ - x: Math.floor(xInDevicePixel), - // 视口坐标系原点在左上,而 WebGL 在左下,需要翻转 Y 轴 - y: Math.floor(height - (y + 1) * DOM.DPR), - width: 1, - height: 1, - data: new Uint8Array(1 * 1 * 4), - }); - return pickedColors; -} - export function isTileLoaded(tile: Tile) { return tile.layerIDList.length === tile.loadedLayers; } export function isTileChildLoaded(tile: Tile) { - const childs = tile.children; - return childs.filter((child) => isTileLoaded(child)).length === childs.length; + const children = tile.children; + return ( + children.filter((child) => isTileLoaded(child)).length === children.length + ); } export function isTileParentLoaded(tile: Tile) { @@ -167,21 +99,6 @@ export function tileAllLoad(tile: Tile, callback: () => void) { }, 36); } -export function updateLayersConfig(layers: ILayer[], key: string, value: any) { - layers.map((layer) => { - if (key === 'mask') { - // Tip: 栅格瓦片生效、设置全局的 mask、瓦片被全局的 mask 影响 - layer.style({ - mask: value, - }); - } else { - layer.updateLayerConfig({ - [key]: value, - }); - } - }); -} - function dispatchTileVisibleChange(tile: Tile, callback: () => void) { if (tile.isVisible) { callback();