feat: 瓦片性能和代码优化 (#1347)

* fix: 修复 featureScale 错误

* style: lint style

* fix: remove featureScalePlugin async

* feat: 优化数据栅格瓦片的渲染、瓦片管理流程完善

* style: lint style

Co-authored-by: shihui <yiqianyao.yqy@alibaba-inc.com>
This commit is contained in:
YiQianYao 2022-09-20 17:41:00 +08:00 committed by GitHub
parent c95f70fa92
commit a2e525e12b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 304 additions and 253 deletions

View File

@ -109,6 +109,16 @@ export default () => {
});
scene.addLayer(layer);
// setTimeout(() => {
// layer.style({
// rampColors: {
// colors: ['#f00', '#ff0'],
// positions: [0, 1]
// }
// });
// scene.render()
// }, 2000)
});
});

View File

@ -1,6 +1,6 @@
// @ts-ignore
import { SyncBailHook, SyncHook, SyncWaterfallHook } from '@antv/async-hook';
import { IColorRamp, IImagedata, Tile, TilesetManager } from '@antv/l7-utils';
import { IColorRamp, Tile, TilesetManager } from '@antv/l7-utils';
import { Container } from 'inversify';
import Clock from '../../utils/clock';
import { ISceneConfig } from '../config/IConfigService';
@ -37,10 +37,6 @@ import {
Triangulation,
} from './IStyleAttributeService';
// import {
// IStyleAttributeUpdateOptions,
// StyleAttributeField,
// } from '@antv/l7-core';y
export enum BlendType {
normal = 'normal',
additive = 'additive',
@ -162,7 +158,6 @@ export interface ISubLayerInitOptions {
rampColors?: IColorRamp;
colorTexture?: ITexture2D;
// 在初始化的时候使用
rampColorsData?: ImageData | IImagedata;
pixelConstant?: number;
pixelConstantR?: number;
@ -185,6 +180,7 @@ export interface ITilePickManager {
beforeSelect(pickedColors: any): void;
clearPick(): void;
pickRender(layers: ILayer[], target: IInteractionTarget): boolean;
destroy(): void;
}
export interface IBaseTileLayerManager {
@ -202,12 +198,13 @@ export interface IBaseTileLayerManager {
clearChild(): void;
hasChild(layer: ILayer): boolean;
render(isPicking?: boolean): void;
updateLayersConfig(layers: ILayer[], key: string, value: any): void;
destroy(): void;
}
export interface ITileLayerManager extends IBaseTileLayerManager{
tilePickManager: ITilePickManager;
pickLayers(target: IInteractionTarget): boolean;
destroy(): void;
}
export interface IBaseTileLayer {
@ -219,12 +216,14 @@ export interface IBaseTileLayer {
children: ILayer[];
scaleField: any;
render(isPicking?: boolean): void;
destroy(): void;
}
export interface ITileLayer extends IBaseTileLayer{
tileLayerManager: ITileLayerManager;
pickLayers(target: IInteractionTarget): boolean;
clearPick(type: string): void;
clearPickState(): void;
destroy(): void;
}
export interface ITileLayerOPtions {

View File

@ -977,10 +977,11 @@ export default class BaseLayer<ChildLayerStyleOptions = {}>
// 执行每个图层单独的 clearModels 方法 (清除一些额外的 texture、program、buffer 等)
this.hooks.afterDestroy.call();
// Tip: 清除各个图层自定义的 models 资源
this.layerModel?.clearModels(refresh);
this.tileLayer?.destroy();
this.models = [];
this.layerService.cleanRemove(this, refresh);

View File

@ -272,6 +272,7 @@ export interface IHeatMapLayerStyleOptions extends IBaseLayerStyleOptions {
}
export interface IRasterLayerStyleOptions extends IBaseLayerStyleOptions {
colorTexture?: ITexture2D;
domain: [number, number];
noDataValue: number;
clampLow: boolean;

View File

@ -50,6 +50,5 @@ export default class RaterLayer extends BaseLayer<IRasterLayerStyleOptions> {
default:
return 'raster';
}
// return 'raster';
}
}

View File

@ -0,0 +1,108 @@
import {
AttributeType,
gl,
IEncodeFeature,
IModel,
ITexture2D,
} from '@antv/l7-core';
import { getMask } from '@antv/l7-utils';
import BaseModel from '../../core/BaseModel';
import { IRasterLayerStyleOptions } from '../../core/interface';
import { RasterImageTriangulation } from '../../core/triangulation';
import rasterFrag from '../shaders/raster_2d_frag.glsl';
import rasterVert from '../shaders/raster_2d_vert.glsl';
export default class RasterModel extends BaseModel {
protected texture: ITexture2D;
public getUninforms() {
const { createTexture2D } = this.rendererService;
const {
colorTexture = createTexture2D({
data: [],
width: 0,
height: 0,
flipY: false,
}),
opacity = 1,
clampLow = true,
clampHigh = true,
noDataValue = -9999999,
domain = [0, 1],
} = this.layer.getLayerConfig() as IRasterLayerStyleOptions;
return {
u_opacity: opacity || 1,
u_texture: this.texture,
u_domain: domain,
u_clampLow: clampLow,
u_clampHigh: typeof clampHigh !== 'undefined' ? clampHigh : clampLow,
u_noDataValue: noDataValue,
u_colorTexture: colorTexture,
};
}
public initModels(callbackModel: (models: IModel[]) => void) {
const {
mask = false,
maskInside = true,
} = this.layer.getLayerConfig() as IRasterLayerStyleOptions;
const source = this.layer.getSource();
const { createTexture2D } = this.rendererService;
const parserDataItem = source.data.dataArray[0];
this.texture = createTexture2D({
data: parserDataItem.data,
width: parserDataItem.width,
height: parserDataItem.height,
format: gl.LUMINANCE,
type: gl.FLOAT,
});
this.layer
.buildLayerModel({
moduleName: 'rasterTileImageData',
vertexShader: rasterVert,
fragmentShader: rasterFrag,
triangulation: RasterImageTriangulation,
depth: { enable: false },
stencil: getMask(mask, maskInside),
pick: false,
})
.then((model) => {
callbackModel([model]);
})
.catch((err) => {
console.warn(err);
callbackModel([]);
});
}
public buildModels(callbackModel: (models: IModel[]) => void) {
this.initModels(callbackModel);
}
public clearModels(): void {
this.texture?.destroy();
}
protected registerBuiltinAttributes() {
this.styleAttributeService.registerStyleAttribute({
name: 'uv',
type: AttributeType.Attribute,
descriptor: {
name: 'a_Uv',
buffer: {
usage: gl.DYNAMIC_DRAW,
data: [],
type: gl.FLOAT,
},
size: 2,
update: (
feature: IEncodeFeature,
featureIdx: number,
vertex: number[],
) => {
return [vertex[3], vertex[4]];
},
},
});
}
}

View File

@ -23,6 +23,4 @@ void main() {
gl_FragColor = color;
gl_FragColor.a = gl_FragColor.a * u_opacity ;
}
}

View File

@ -8,7 +8,6 @@ varying vec2 v_texCoord;
void main() {
v_texCoord = a_Uv;
vec4 project_pos = project_position(vec4(a_Position, 1.0));
gl_Position = project_common_position_to_clipspace(vec4(project_pos.xy,0., 1.0));
if(u_CoordinateSystem == COORDINATE_SYSTEM_P20_2) { // gaode2.x
gl_Position = u_Mvp * (vec4(project_pos.xy,0., 1.0));

View File

@ -5,100 +5,26 @@ import {
ISubLayerInitOptions,
IBaseTileLayerManager,
} from '@antv/l7-core';
import { Tile } from '@antv/l7-utils';
import { getTileFactory, ITileFactory, TileType } from '../tileFactory';
import { TileManager } from './baseTileManager';
import { getLayerShape, getMaskValue } from '../utils';
export class BaseMapTileLayerManager implements IBaseTileLayerManager {
export class BaseMapTileLayerManager extends TileManager implements IBaseTileLayerManager {
// only support vector layer
public sourceLayer: string;
public parent: ILayer;
public children: ILayer[];
public mapService: IMapService;
public rendererService: IRendererService;
private tileFactory: ITileFactory;
private initOptions: ISubLayerInitOptions;
constructor(
parent: ILayer,
mapService: IMapService,
rendererService: IRendererService,
) {
super();
this.parent = parent;
this.children = parent.layerChildren;
this.mapService = mapService;
this.rendererService = rendererService;
this.setSubLayerInitOptipn();
this.setSubLayerInitOption();
this.initTileFactory();
}
public createTile(tile: Tile) {
return this.tileFactory.createTile(tile, this.initOptions);
}
public 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,
});
}
});
}
public addChild(layer: ILayer) {
this.children.push(layer);
}
public addChilds(layers: ILayer[]) {
this.children.push(...layers);
}
public removeChilds(layerIDList: string[], refresh = true) {
const remveLayerList: ILayer[] = [];
const cacheLayerList: ILayer[] = [];
this.children.filter((child) => {
layerIDList.includes(child.id)
? remveLayerList.push(child)
: cacheLayerList.push(child);
});
remveLayerList.map((layer) => layer.destroy(refresh));
this.children = cacheLayerList;
}
public removeChild(layer: ILayer) {
const layerIndex = this.children.indexOf(layer);
if (layerIndex > -1) {
this.children.splice(layerIndex, 1);
}
layer.destroy();
}
public getChilds(layerIDList: string[]) {
return this.children.filter((child) => layerIDList.includes(child.id));
}
public getChild(layerID: string) {
return this.children.filter((child) => child.id === layerID)[0];
}
public clearChild() {
this.children.forEach((layer: any) => {
layer.destroy();
});
this.children.slice(0, this.children.length);
}
public hasChild(layer: ILayer) {
return this.children.includes(layer);
}
public render(): void {
this.children
.filter((layer) => layer.inited)
@ -125,7 +51,7 @@ export class BaseMapTileLayerManager implements IBaseTileLayerManager {
});
}
private setSubLayerInitOptipn() {
private setSubLayerInitOption() {
const {
zIndex = 0,
opacity = 1,
@ -166,26 +92,4 @@ export class BaseMapTileLayerManager implements IBaseTileLayerManager {
};
}
private getSourceLayer(parentParserType: string, sourceLayer: string|undefined) {
if(parentParserType === 'geojsonvt') {
return 'geojsonvt';
} else if(parentParserType === 'testTile') {
return 'testTile';
} else {
return sourceLayer;
}
}
private initTileFactory() {
const source = this.parent.getSource();
const TileFactory = getTileFactory(
this.parent.type as TileType,
source.parser,
);
this.tileFactory = new TileFactory({
parent: this.parent,
mapService: this.mapService,
rendererService: this.rendererService,
});
}
}

View File

@ -0,0 +1,96 @@
import {
ILayer,
IRendererService,
IMapService,
ISubLayerInitOptions,
} from '@antv/l7-core';
import { Tile } from '@antv/l7-utils';
import { ITileFactory, getTileFactory, TileType } from '../tileFactory';
export class TileManager {
public sourceLayer: string;
public parent: ILayer;
public children: ILayer[];
public mapService: IMapService;
public rendererService: IRendererService;
protected tileFactory: ITileFactory;
protected initOptions: ISubLayerInitOptions;
public createTile(tile: Tile) {
return this.tileFactory.createTile(tile, this.initOptions);
}
public addChild(layer: ILayer) {
this.children.push(layer);
}
public addChilds(layers: ILayer[]) {
this.children.push(...layers);
}
public removeChilds(layerIDList: string[], refresh = true) {
const remveLayerList: ILayer[] = [];
const cacheLayerList: ILayer[] = [];
this.children.filter((child) => {
layerIDList.includes(child.id)
? remveLayerList.push(child)
: cacheLayerList.push(child);
});
remveLayerList.map((layer) => layer.destroy(refresh));
this.children = cacheLayerList;
}
public removeChild(layer: ILayer) {
const layerIndex = this.children.indexOf(layer);
if (layerIndex > -1) {
this.children.splice(layerIndex, 1);
}
layer.destroy();
}
public getChilds(layerIDList: string[]) {
return this.children.filter((child) => layerIDList.includes(child.id));
}
public getChild(layerID: string) {
return this.children.filter((child) => child.id === layerID)[0];
}
public clearChild() {
this.children.forEach((layer: any) => {
layer.destroy();
});
this.children.slice(0, this.children.length);
}
public hasChild(layer: ILayer) {
return this.children.includes(layer);
}
public initTileFactory() {
const source = this.parent.getSource();
const TileFactory = getTileFactory(
this.parent.type as TileType,
source.parser,
);
this.tileFactory = new TileFactory({
parent: this.parent,
mapService: this.mapService,
rendererService: this.rendererService,
});
}
public getSourceLayer(parentParserType: string, sourceLayer: string|undefined) {
if(parentParserType === 'geojsonvt') {
return 'geojsonvt';
} else if(parentParserType === 'testTile') {
return 'testTile';
} else {
return sourceLayer;
}
}
public destroy(): void {
}
}

View File

@ -10,22 +10,14 @@ import {
ITransform,
ScaleAttributeType,
} from '@antv/l7-core';
import { generateColorRamp, IColorRamp, Tile } from '@antv/l7-utils';
import { getTileFactory, ITileFactory, TileType } from '../tileFactory';
import { getLayerShape, getMaskValue } from '../utils';
import { TileManager } from './baseTileManager';
import { generateColorRamp, IColorRamp } from '@antv/l7-utils';
import { getLayerShape, getMaskValue, updateLayersConfig } from '../utils';
import TileConfigManager, { ITileConfigManager } from './tileConfigManager';
import TilePickManager from './tilePickerManager';
export class TileLayerManager implements ITileLayerManager {
public sourceLayer: string;
public parent: ILayer;
public children: ILayer[];
public mapService: IMapService;
public rendererService: IRendererService;
export class TileLayerManager extends TileManager implements ITileLayerManager {
public tilePickManager: ITilePickManager;
public tileConfigManager: ITileConfigManager;
private tileFactory: ITileFactory;
private initOptions: ISubLayerInitOptions;
private rampColorsData: any;
private transforms: ITransform[];
constructor(
parent: ILayer,
@ -34,6 +26,7 @@ export class TileLayerManager implements ITileLayerManager {
pickingService: IPickingService,
transforms: ITransform[]
) {
super();
this.parent = parent;
this.children = parent.layerChildren;
this.mapService = mapService;
@ -48,78 +41,11 @@ export class TileLayerManager implements ITileLayerManager {
);
this.tileConfigManager = new TileConfigManager();
this.setSubLayerInitOptipn();
this.setSubLayerInitOption();
this.setConfigListener();
this.initTileFactory();
}
public createTile(tile: Tile) {
return this.tileFactory.createTile(tile, this.initOptions);
}
public 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,
});
}
});
}
public addChild(layer: ILayer) {
this.children.push(layer);
}
public addChilds(layers: ILayer[]) {
this.children.push(...layers);
}
public removeChilds(layerIDList: string[], refresh = true) {
const remveLayerList: ILayer[] = [];
const cacheLayerList: ILayer[] = [];
this.children.filter((child) => {
layerIDList.includes(child.id)
? remveLayerList.push(child)
: cacheLayerList.push(child);
});
remveLayerList.map((layer) => layer.destroy(refresh));
this.children = cacheLayerList;
}
public removeChild(layer: ILayer) {
const layerIndex = this.children.indexOf(layer);
if (layerIndex > -1) {
this.children.splice(layerIndex, 1);
}
layer.destroy();
}
public getChilds(layerIDList: string[]) {
return this.children.filter((child) => layerIDList.includes(child.id));
}
public getChild(layerID: string) {
return this.children.filter((child) => child.id === layerID)[0];
}
public clearChild() {
this.children.forEach((layer: any) => {
layer.destroy();
});
this.children.slice(0, this.children.length);
}
public hasChild(layer: ILayer) {
return this.children.includes(layer);
}
public render(): void {
this.tileConfigManager?.checkConfig(this.parent);
this.tilePickManager?.normalRender(this.children);
@ -129,7 +55,7 @@ export class TileLayerManager implements ITileLayerManager {
return this.tilePickManager?.pickRender(this.children, target);
}
private setSubLayerInitOptipn() {
private setSubLayerInitOption() {
const {
zIndex = 0,
opacity = 1,
@ -175,19 +101,6 @@ export class TileLayerManager implements ITileLayerManager {
const parentParserType = source.getParserType();
const layerShape = getLayerShape(this.parent.type, this.parent);
let colorTexture = undefined;
if (rampColors) {
// 构建统一的色带贴图
const { createTexture2D } = this.rendererService;
this.rampColorsData = generateColorRamp(rampColors as IColorRamp);
const imageData = generateColorRamp(rampColors as IColorRamp);
colorTexture = createTexture2D({
data: this.rampColorsData.data,
width: imageData.width,
height: imageData.height,
flipY: false,
});
}
this.initOptions = {
@ -210,8 +123,6 @@ export class TileLayerManager implements ITileLayerManager {
clampHigh,
domain,
rampColors,
rampColorsData: this.rampColorsData,
colorTexture,
// worker
workerEnabled,
@ -221,15 +132,17 @@ export class TileLayerManager implements ITileLayerManager {
pixelConstantB,
pixelConstantRGB,
};
}
private getSourceLayer(parentParserType: string, sourceLayer: string|undefined) {
if(parentParserType === 'geojsonvt') {
return 'geojsonvt';
} else if(parentParserType === 'testTile') {
return 'testTile';
} else {
return sourceLayer;
if (rampColors) {
// 构建统一的色带贴图
const { createTexture2D } = this.rendererService;
const imageData = generateColorRamp(rampColors as IColorRamp) as ImageData;
const colorTexture = createTexture2D({
data: imageData.data,
width: imageData.width,
height: imageData.height,
flipY: false,
});
this.initOptions.colorTexture = colorTexture;
}
}
@ -327,25 +240,24 @@ export class TileLayerManager implements ITileLayerManager {
// @ts-ignore
const config = layerConfig[style];
updateValue = config;
this.updateLayersConfig(this.children, style, config);
updateLayersConfig(this.children, style, config);
if (style === 'rampColors' && config) {
this.rampColorsData = generateColorRamp(config as IColorRamp);
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);
}
}
// @ts-ignore
this.initOptions[style] = updateValue;
}
private initTileFactory() {
const source = this.parent.getSource();
const TileFactory = getTileFactory(
this.parent.type as TileType,
source.parser,
);
this.tileFactory = new TileFactory({
parent: this.parent,
mapService: this.mapService,
rendererService: this.rendererService,
});
public destroy(): void {
this.tilePickManager.destroy();
}
}

View File

@ -162,4 +162,8 @@ export default class TilePickManager extends EventEmitter
layer.hooks.afterRender.call();
}
}
public destroy(): void {
this.removeAllListeners();
}
}

View File

@ -34,6 +34,9 @@ export default class TileModel extends BaseModel {
return this.buildModels();
}
public clearModels(): void {
}
public buildModels() {
return [];
}

View File

@ -15,17 +15,16 @@ export default class RasterTiffTile extends TileFactory {
public createTile(tile: Tile, initOptions: ISubLayerInitOptions) {
const {
colorTexture,
opacity,
domain,
clampHigh,
clampLow,
rampColors,
rampColorsData,
mask,
} = initOptions;
const rasterdata = tile.data;
if (!rasterdata.data) {
const rasterData = tile.data;
if (!rasterData.data) {
console.warn('raster data not exist!');
return {
layers: [],
@ -36,21 +35,20 @@ export default class RasterTiffTile extends TileFactory {
visible: tile.isVisible,
mask,
})
.source(rasterdata.data, {
.source(rasterData.data, {
parser: {
type: 'raster',
width: rasterdata.width,
height: rasterdata.height,
width: rasterData.width,
height: rasterData.height,
extent: tile.bboxPolygon.bbox,
},
})
.style({
colorTexture,
opacity,
domain,
clampHigh,
clampLow,
rampColors,
rampColorsData,
});
this.emitEvent([layer], false);

View File

@ -1,6 +1,6 @@
import BaseLayer from '../../core/BaseLayer';
import { IRasterLayerStyleOptions } from '../../core/interface';
import RasterModel from '../../raster/models/raster';
import RasterModel from '../../raster/models/rasterTile';
export default class RasterTiffLayer extends BaseLayer<
Partial<IRasterLayerStyleOptions>

View File

@ -176,4 +176,8 @@ export default class BaseTileLayer implements IBaseTileLayer {
return { latLonBounds, zoom };
}
public destroy() {
this.tilesetManager?.destroy();
}
}

View File

@ -109,6 +109,7 @@ export default class BaseTileLayer implements ITileLayer {
return this.tileLayerManager.pickLayers(target);
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
public tileLoaded(tile: Tile) {
//
}
@ -331,6 +332,7 @@ export default class BaseTileLayer implements ITileLayer {
return;
}
// 瓦片数据加载成功
// eslint-disable-next-line @typescript-eslint/no-unused-vars
this.tilesetManager.on('tile-loaded', (tile: Tile) => {
// 将事件抛出,图层上可以监听使用
});
@ -342,6 +344,7 @@ export default class BaseTileLayer implements ITileLayer {
});
// 瓦片数据加载失败
// eslint-disable-next-line @typescript-eslint/no-unused-vars
this.tilesetManager.on('tile-error', (error, tile: Tile) => {
// 将事件抛出,图层上可以监听使用
this.tileError(error);
@ -372,4 +375,9 @@ export default class BaseTileLayer implements ITileLayer {
return { latLonBounds, zoom };
}
public destroy() {
this.tilesetManager?.destroy();
this.tileLayerManager.destroy();
}
}

View File

@ -1,7 +1,7 @@
import { ILayer } from '@antv/l7-core';
import { Tile } from '@antv/l7-utils';
import BaseTileLayer from './tileLayer/baseMapTileLayer';
import { tileAllLoad } from './utils';
import { tileAllLoad, updateLayersConfig } from './utils';
export class TMSBaseMapTileLayer extends BaseTileLayer {
public type: string = 'BaseMapTMS';
@ -58,11 +58,7 @@ export class TMSBaseMapTileLayer extends BaseTileLayer {
private updateTileVisible(tile: Tile, layers: ILayer[]) {
this.emitTileVisibleEvent(tile, () => {
this.tileLayerManager.updateLayersConfig(
layers,
'visible',
tile.isVisible,
);
updateLayersConfig(layers, 'visible', tile.isVisible);
this.layerService.reRender();
});
}

View File

@ -1,7 +1,7 @@
import { ILayer } from '@antv/l7-core';
import { Tile } from '@antv/l7-utils';
import BaseTileLayer from './tileLayer/baseTileLayer';
import { tileAllLoad } from './utils';
import { tileAllLoad, updateLayersConfig } from './utils';
export class TMSTileLayer extends BaseTileLayer {
public type: string = 'TMS';
@ -60,11 +60,7 @@ export class TMSTileLayer extends BaseTileLayer {
private updateTileVisible(tile: Tile, layers: ILayer[]) {
this.emitTileVisibleEvent(tile, () => {
this.tileLayerManager.updateLayersConfig(
layers,
'visible',
tile.isVisible,
);
updateLayersConfig(layers, 'visible', tile.isVisible);
this.layerService.reRender();
});
}

View File

@ -165,3 +165,18 @@ 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,
});
}
});
}