mirror of https://gitee.com/antv-l7/antv-l7
feat: 矢量文本计算优化、性能优化 (#1310)
* feat: 新增测试瓦片图层 * style: lint style * feat: 矢量文本图层性能优化 * chore: change tiletestlayer demo * style: lint style * feat: 封装 TileDebugLayer 的source 模块,优化图层默认数据的配置,测试图层样式调整 * style: line style Co-authored-by: shihui <yiqianyao.yqy@alibaba-inc.com>
This commit is contained in:
parent
7837021908
commit
f345fba72c
|
@ -0,0 +1,2 @@
|
||||||
|
### Test Tile
|
||||||
|
<code src="./testTile.tsx"></code>
|
|
@ -0,0 +1,33 @@
|
||||||
|
import { Scene, TileDebugLayer } from '@antv/l7';
|
||||||
|
import { Mapbox } from '@antv/l7-maps';
|
||||||
|
import React, { useEffect } from 'react';
|
||||||
|
|
||||||
|
export default () => {
|
||||||
|
useEffect(() => {
|
||||||
|
const scene = new Scene({
|
||||||
|
id: 'map',
|
||||||
|
// stencil: true,
|
||||||
|
map: new Mapbox({
|
||||||
|
center: [121.268, 30.3628],
|
||||||
|
pitch: 0,
|
||||||
|
// style: 'blank',
|
||||||
|
zoom: 4,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
const layer = new TileDebugLayer();
|
||||||
|
|
||||||
|
scene.on('loaded', () => {
|
||||||
|
scene.addLayer(layer);
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
id="map"
|
||||||
|
style={{
|
||||||
|
height: '500px',
|
||||||
|
position: 'relative',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
|
@ -265,6 +265,10 @@ export interface ILayer {
|
||||||
masks: ILayer[]; // 图层的 mask 列表
|
masks: ILayer[]; // 图层的 mask 列表
|
||||||
sceneContainer: Container | undefined;
|
sceneContainer: Container | undefined;
|
||||||
dataState: IDataState; // 数据流状态
|
dataState: IDataState; // 数据流状态
|
||||||
|
defaultSourceConfig: {
|
||||||
|
data: any[],
|
||||||
|
options: ISourceCFG | undefined,
|
||||||
|
},
|
||||||
pickedFeatureID: number | null;
|
pickedFeatureID: number | null;
|
||||||
hooks: {
|
hooks: {
|
||||||
init: SyncBailHook;
|
init: SyncBailHook;
|
||||||
|
|
|
@ -81,6 +81,11 @@ export default class BaseLayer<ChildLayerStyleOptions = {}>
|
||||||
public layerType?: string | undefined;
|
public layerType?: string | undefined;
|
||||||
public triangulation?: Triangulation | undefined;
|
public triangulation?: Triangulation | undefined;
|
||||||
|
|
||||||
|
public defaultSourceConfig: {
|
||||||
|
data: any[];
|
||||||
|
options: ISourceCFG | undefined;
|
||||||
|
};
|
||||||
|
|
||||||
public dataState: IDataState = {
|
public dataState: IDataState = {
|
||||||
dataSourceNeedUpdate: false,
|
dataSourceNeedUpdate: false,
|
||||||
dataMappingNeedUpdate: false,
|
dataMappingNeedUpdate: false,
|
||||||
|
|
|
@ -16,6 +16,8 @@ import EarthLayer from './earth';
|
||||||
import MaskLayer from './mask';
|
import MaskLayer from './mask';
|
||||||
import WindLayer from './wind';
|
import WindLayer from './wind';
|
||||||
|
|
||||||
|
import TileDebugLayer from './tile/tileTest';
|
||||||
|
|
||||||
// import ConfigSchemaValidationPlugin from './plugins/ConfigSchemaValidationPlugin';
|
// import ConfigSchemaValidationPlugin from './plugins/ConfigSchemaValidationPlugin';
|
||||||
import DataMappingPlugin from './plugins/DataMappingPlugin';
|
import DataMappingPlugin from './plugins/DataMappingPlugin';
|
||||||
import DataSourcePlugin from './plugins/DataSourcePlugin';
|
import DataSourcePlugin from './plugins/DataSourcePlugin';
|
||||||
|
@ -151,6 +153,7 @@ export {
|
||||||
EarthLayer,
|
EarthLayer,
|
||||||
WindLayer,
|
WindLayer,
|
||||||
MaskLayer,
|
MaskLayer,
|
||||||
|
TileDebugLayer
|
||||||
};
|
};
|
||||||
|
|
||||||
export * from './core/interface';
|
export * from './core/interface';
|
||||||
|
|
|
@ -5,6 +5,25 @@ import { isVectorTile } from '../tile/utils';
|
||||||
|
|
||||||
export default class LineLayer extends BaseLayer<ILineLayerStyleOptions> {
|
export default class LineLayer extends BaseLayer<ILineLayerStyleOptions> {
|
||||||
public type: string = 'LineLayer';
|
public type: string = 'LineLayer';
|
||||||
|
public defaultSourceConfig = {
|
||||||
|
data: [
|
||||||
|
{
|
||||||
|
lng1: 100,
|
||||||
|
lat1: 30.0,
|
||||||
|
lng2: 130,
|
||||||
|
lat2: 30,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
options: {
|
||||||
|
parser: {
|
||||||
|
type: 'json',
|
||||||
|
x: 'lng1',
|
||||||
|
y: 'lat1',
|
||||||
|
x1: 'lng2',
|
||||||
|
y1: 'lat2',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
public buildModels() {
|
public buildModels() {
|
||||||
const shape = this.getModelType();
|
const shape = this.getModelType();
|
||||||
|
|
|
@ -4,6 +4,14 @@ import MaskModels, { MaskModelType } from './models';
|
||||||
|
|
||||||
export default class MaskLayer extends BaseLayer<IMaskLayerStyleOptions> {
|
export default class MaskLayer extends BaseLayer<IMaskLayerStyleOptions> {
|
||||||
public type: string = 'MaskLayer';
|
public type: string = 'MaskLayer';
|
||||||
|
public defaultSourceConfig: {
|
||||||
|
data: [];
|
||||||
|
options: {
|
||||||
|
parser: {
|
||||||
|
type: 'geojson';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
public buildModels() {
|
public buildModels() {
|
||||||
const shape = this.getModelType();
|
const shape = this.getModelType();
|
||||||
this.layerModel = new MaskModels[shape](this);
|
this.layerModel = new MaskModels[shape](this);
|
||||||
|
|
|
@ -1,9 +1,5 @@
|
||||||
import { ILayer, ILayerPlugin, IMapService, TYPES } from '@antv/l7-core';
|
import { ILayer, ILayerPlugin, IMapService, TYPES } from '@antv/l7-core';
|
||||||
import Source, {
|
import Source from '@antv/l7-source';
|
||||||
DEFAULT_DATA,
|
|
||||||
DEFAULT_PARSER,
|
|
||||||
DEFAULT_SOURCE,
|
|
||||||
} from '@antv/l7-source';
|
|
||||||
import { injectable } from 'inversify';
|
import { injectable } from 'inversify';
|
||||||
import 'reflect-metadata';
|
import 'reflect-metadata';
|
||||||
|
|
||||||
|
@ -15,14 +11,9 @@ export default class DataSourcePlugin implements ILayerPlugin {
|
||||||
layer.hooks.init.tap('DataSourcePlugin', () => {
|
layer.hooks.init.tap('DataSourcePlugin', () => {
|
||||||
let source = layer.getSource();
|
let source = layer.getSource();
|
||||||
if (!source) {
|
if (!source) {
|
||||||
// TODO: 允许用户不使用 layer 的 source 方法,在这里传入一个默认的替换的默认数据
|
// Tip: 用户没有传入 source 的时候使用图层的默认数据
|
||||||
const defaultSourceConfig = DEFAULT_SOURCE[
|
const { data, options } =
|
||||||
layer.type as 'PointLayer' | 'LineLayer'
|
layer.sourceOption || layer.defaultSourceConfig;
|
||||||
] || {
|
|
||||||
data: DEFAULT_DATA,
|
|
||||||
options: DEFAULT_PARSER,
|
|
||||||
};
|
|
||||||
const { data, options } = layer.sourceOption || defaultSourceConfig;
|
|
||||||
source = new Source(data, options);
|
source = new Source(data, options);
|
||||||
layer.setSource(source);
|
layer.setSource(source);
|
||||||
}
|
}
|
||||||
|
@ -31,7 +22,6 @@ export default class DataSourcePlugin implements ILayerPlugin {
|
||||||
} else {
|
} else {
|
||||||
source.once('sourceUpdate', () => {
|
source.once('sourceUpdate', () => {
|
||||||
this.updateClusterData(layer);
|
this.updateClusterData(layer);
|
||||||
// TODO: layer.hooks.init.call();
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
// this.updateClusterData(layer);
|
// this.updateClusterData(layer);
|
||||||
|
|
|
@ -6,6 +6,17 @@ import { isVectorTile } from '../tile/utils';
|
||||||
|
|
||||||
export default class PointLayer extends BaseLayer<IPointLayerStyleOptions> {
|
export default class PointLayer extends BaseLayer<IPointLayerStyleOptions> {
|
||||||
public type: string = 'PointLayer';
|
public type: string = 'PointLayer';
|
||||||
|
public defaultSourceConfig = {
|
||||||
|
data: [],
|
||||||
|
options: {
|
||||||
|
parser: {
|
||||||
|
type: 'json',
|
||||||
|
x: 'lng',
|
||||||
|
y: 'lat',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
public buildModels() {
|
public buildModels() {
|
||||||
const modelType = this.getModelType();
|
const modelType = this.getModelType();
|
||||||
this.layerModel = new PointModels[modelType](this);
|
this.layerModel = new PointModels[modelType](this);
|
||||||
|
@ -26,7 +37,7 @@ export default class PointLayer extends BaseLayer<IPointLayerStyleOptions> {
|
||||||
public getModelTypeWillEmptyData(): PointType {
|
public getModelTypeWillEmptyData(): PointType {
|
||||||
if (this.shapeOption) {
|
if (this.shapeOption) {
|
||||||
const { field, values } = this.shapeOption;
|
const { field, values } = this.shapeOption;
|
||||||
const { shape2d, shape3d } = this.getLayerConfig();
|
const { shape2d } = this.getLayerConfig();
|
||||||
|
|
||||||
const iconMap = this.iconService.getIconMap();
|
const iconMap = this.iconService.getIconMap();
|
||||||
|
|
||||||
|
@ -72,6 +83,7 @@ export default class PointLayer extends BaseLayer<IPointLayerStyleOptions> {
|
||||||
},
|
},
|
||||||
vectorpoint: {},
|
vectorpoint: {},
|
||||||
tile: {},
|
tile: {},
|
||||||
|
tileText: {},
|
||||||
earthFill: {},
|
earthFill: {},
|
||||||
earthExtrude: {},
|
earthExtrude: {},
|
||||||
};
|
};
|
||||||
|
@ -79,20 +91,6 @@ export default class PointLayer extends BaseLayer<IPointLayerStyleOptions> {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected getModelType(): PointType {
|
protected getModelType(): PointType {
|
||||||
const PointTypes = [
|
|
||||||
'fillImage',
|
|
||||||
'fill',
|
|
||||||
'radar',
|
|
||||||
'image',
|
|
||||||
'normal',
|
|
||||||
'simplePoint',
|
|
||||||
'extrude',
|
|
||||||
'text',
|
|
||||||
'vectorpoint',
|
|
||||||
'tile',
|
|
||||||
'earthFill',
|
|
||||||
'earthExtrude',
|
|
||||||
];
|
|
||||||
const parserType = this.layerSource.getParserType();
|
const parserType = this.layerSource.getParserType();
|
||||||
if (isVectorTile(parserType)) {
|
if (isVectorTile(parserType)) {
|
||||||
return 'vectorpoint';
|
return 'vectorpoint';
|
||||||
|
|
|
@ -10,6 +10,7 @@ import NormalModel from './normal';
|
||||||
import Radar from './radar';
|
import Radar from './radar';
|
||||||
import SimplePopint from './simplePoint';
|
import SimplePopint from './simplePoint';
|
||||||
import TextModel from './text';
|
import TextModel from './text';
|
||||||
|
import TileTextModel from './tileText';
|
||||||
import TileFillModel from './tile';
|
import TileFillModel from './tile';
|
||||||
|
|
||||||
export type PointType =
|
export type PointType =
|
||||||
|
@ -23,6 +24,7 @@ export type PointType =
|
||||||
| 'text'
|
| 'text'
|
||||||
| 'vectorpoint'
|
| 'vectorpoint'
|
||||||
| 'tile'
|
| 'tile'
|
||||||
|
| 'tileText'
|
||||||
| 'earthFill'
|
| 'earthFill'
|
||||||
| 'earthExtrude';
|
| 'earthExtrude';
|
||||||
|
|
||||||
|
@ -37,6 +39,7 @@ const PointModels: { [key in PointType]: any } = {
|
||||||
text: TextModel,
|
text: TextModel,
|
||||||
vectorpoint: PointTileModel,
|
vectorpoint: PointTileModel,
|
||||||
tile: TileFillModel,
|
tile: TileFillModel,
|
||||||
|
tileText: TileTextModel,
|
||||||
earthFill: EarthFillModel,
|
earthFill: EarthFillModel,
|
||||||
earthExtrude: EarthExtrudeModel,
|
earthExtrude: EarthExtrudeModel,
|
||||||
};
|
};
|
||||||
|
|
|
@ -0,0 +1,530 @@
|
||||||
|
import {
|
||||||
|
AttributeType,
|
||||||
|
gl,
|
||||||
|
IEncodeFeature,
|
||||||
|
IModel,
|
||||||
|
IModelUniform,
|
||||||
|
ITexture2D,
|
||||||
|
} from '@antv/l7-core';
|
||||||
|
import {
|
||||||
|
calculateCentroid,
|
||||||
|
getMask,
|
||||||
|
padBounds,
|
||||||
|
} from '@antv/l7-utils';
|
||||||
|
import { isNumber } from 'lodash';
|
||||||
|
import BaseModel from '../../core/BaseModel';
|
||||||
|
import { IPointLayerStyleOptions } from '../../core/interface';
|
||||||
|
import CollisionIndex from '../../utils/collision-index';
|
||||||
|
import {
|
||||||
|
getGlyphQuads,
|
||||||
|
IGlyphQuad,
|
||||||
|
shapeText,
|
||||||
|
} from '../../utils/symbol-layout';
|
||||||
|
import textFrag from '../shaders/text_frag.glsl';
|
||||||
|
import textVert from '../shaders/text_vert.glsl';
|
||||||
|
|
||||||
|
export function TextTriangulation(feature: IEncodeFeature) {
|
||||||
|
// @ts-ignore
|
||||||
|
const that = this as TextModel;
|
||||||
|
const id = feature.id as number;
|
||||||
|
const vertices: number[] = [];
|
||||||
|
const indices: number[] = [];
|
||||||
|
|
||||||
|
if (!that.glyphInfoMap || !that.glyphInfoMap[id]) {
|
||||||
|
return {
|
||||||
|
vertices: [], // [ x, y, z, tex.x,tex.y, offset.x. offset.y]
|
||||||
|
indices: [],
|
||||||
|
size: 7,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
const centroid = that.glyphInfoMap[id].centroid as number[]; // 计算中心点
|
||||||
|
const coord =
|
||||||
|
centroid.length === 2 ? [centroid[0], centroid[1], 0] : centroid;
|
||||||
|
that.glyphInfoMap[id].glyphQuads.forEach(
|
||||||
|
(quad: IGlyphQuad, index: number) => {
|
||||||
|
vertices.push(
|
||||||
|
...coord,
|
||||||
|
quad.tex.x,
|
||||||
|
quad.tex.y + quad.tex.height,
|
||||||
|
quad.tl.x,
|
||||||
|
quad.tl.y,
|
||||||
|
...coord,
|
||||||
|
quad.tex.x + quad.tex.width,
|
||||||
|
quad.tex.y + quad.tex.height,
|
||||||
|
quad.tr.x,
|
||||||
|
quad.tr.y,
|
||||||
|
...coord,
|
||||||
|
quad.tex.x + quad.tex.width,
|
||||||
|
quad.tex.y,
|
||||||
|
quad.br.x,
|
||||||
|
quad.br.y,
|
||||||
|
...coord,
|
||||||
|
quad.tex.x,
|
||||||
|
quad.tex.y,
|
||||||
|
quad.bl.x,
|
||||||
|
quad.bl.y,
|
||||||
|
);
|
||||||
|
indices.push(
|
||||||
|
0 + index * 4,
|
||||||
|
1 + index * 4,
|
||||||
|
2 + index * 4,
|
||||||
|
2 + index * 4,
|
||||||
|
3 + index * 4,
|
||||||
|
0 + index * 4,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
return {
|
||||||
|
vertices, // [ x, y, z, tex.x,tex.y, offset.x. offset.y]
|
||||||
|
indices,
|
||||||
|
size: 7,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class TextModel extends BaseModel {
|
||||||
|
public glyphInfo: IEncodeFeature[];
|
||||||
|
public glyphInfoMap: {
|
||||||
|
[key: string]: {
|
||||||
|
shaping: any;
|
||||||
|
glyphQuads: IGlyphQuad[];
|
||||||
|
centroid: number[];
|
||||||
|
};
|
||||||
|
} = {};
|
||||||
|
private texture: ITexture2D;
|
||||||
|
private currentZoom: number = -1;
|
||||||
|
private extent: [[number, number], [number, number]];
|
||||||
|
private textureHeight: number = 0;
|
||||||
|
private textCount: number = 0;
|
||||||
|
private preTextStyle: Partial<IPointLayerStyleOptions> = {};
|
||||||
|
public getUninforms(): IModelUniform {
|
||||||
|
const {
|
||||||
|
opacity = 1.0,
|
||||||
|
stroke = '#fff',
|
||||||
|
strokeWidth = 0,
|
||||||
|
textAnchor = 'center',
|
||||||
|
textAllowOverlap = false,
|
||||||
|
halo = 0.5,
|
||||||
|
gamma = 2.0,
|
||||||
|
raisingHeight = 0,
|
||||||
|
} = this.layer.getLayerConfig() as IPointLayerStyleOptions;
|
||||||
|
const { canvas, mapping } = this.fontService;
|
||||||
|
if (Object.keys(mapping).length !== this.textCount) {
|
||||||
|
this.updateTexture();
|
||||||
|
this.textCount = Object.keys(mapping).length;
|
||||||
|
}
|
||||||
|
this.preTextStyle = {
|
||||||
|
textAnchor,
|
||||||
|
textAllowOverlap,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (
|
||||||
|
this.dataTextureTest &&
|
||||||
|
this.dataTextureNeedUpdate({
|
||||||
|
opacity,
|
||||||
|
strokeWidth,
|
||||||
|
stroke,
|
||||||
|
})
|
||||||
|
) {
|
||||||
|
this.judgeStyleAttributes({
|
||||||
|
opacity,
|
||||||
|
strokeWidth,
|
||||||
|
stroke,
|
||||||
|
});
|
||||||
|
|
||||||
|
const encodeData = this.layer.getEncodedData();
|
||||||
|
const { data, width, height } = this.calDataFrame(
|
||||||
|
this.cellLength,
|
||||||
|
encodeData,
|
||||||
|
this.cellProperties,
|
||||||
|
);
|
||||||
|
this.rowCount = height; // 当前数据纹理有多少行
|
||||||
|
|
||||||
|
this.dataTexture =
|
||||||
|
this.cellLength > 0 && data.length > 0
|
||||||
|
? this.createTexture2D({
|
||||||
|
flipY: true,
|
||||||
|
data,
|
||||||
|
format: gl.LUMINANCE,
|
||||||
|
type: gl.FLOAT,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
})
|
||||||
|
: this.createTexture2D({
|
||||||
|
flipY: true,
|
||||||
|
data: [1],
|
||||||
|
format: gl.LUMINANCE,
|
||||||
|
type: gl.FLOAT,
|
||||||
|
width: 1,
|
||||||
|
height: 1,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
u_dataTexture: this.dataTexture, // 数据纹理 - 有数据映射的时候纹理中带数据,若没有任何数据映射时纹理是 [1]
|
||||||
|
u_cellTypeLayout: this.getCellTypeLayout(),
|
||||||
|
u_raisingHeight: Number(raisingHeight),
|
||||||
|
|
||||||
|
u_opacity: isNumber(opacity) ? opacity : 1.0,
|
||||||
|
u_stroke_width: isNumber(strokeWidth) ? strokeWidth : 1.0,
|
||||||
|
u_stroke_color: this.getStrokeColor(stroke),
|
||||||
|
|
||||||
|
u_sdf_map: this.texture,
|
||||||
|
u_halo_blur: halo,
|
||||||
|
u_gamma_scale: gamma,
|
||||||
|
u_sdf_map_size: [canvas.width, canvas.height],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public initModels(callbackModel: (models: IModel[]) => void) {
|
||||||
|
this.layer.on('remapping', this.mapping);
|
||||||
|
this.extent = this.textExtent();
|
||||||
|
const {
|
||||||
|
textAnchor = 'center',
|
||||||
|
textAllowOverlap = true,
|
||||||
|
} = this.layer.getLayerConfig() as IPointLayerStyleOptions;
|
||||||
|
this.preTextStyle = {
|
||||||
|
textAnchor,
|
||||||
|
textAllowOverlap,
|
||||||
|
};
|
||||||
|
this.buildModels(callbackModel);
|
||||||
|
}
|
||||||
|
|
||||||
|
public buildModels = async (callbackModel: (models: IModel[]) => void) => {
|
||||||
|
const {
|
||||||
|
mask = false,
|
||||||
|
maskInside = true,
|
||||||
|
} = this.layer.getLayerConfig() as IPointLayerStyleOptions;
|
||||||
|
this.mapping();
|
||||||
|
|
||||||
|
this.layer
|
||||||
|
.buildLayerModel({
|
||||||
|
moduleName: 'pointText',
|
||||||
|
vertexShader: textVert,
|
||||||
|
fragmentShader: textFrag,
|
||||||
|
triangulation: TextTriangulation.bind(this),
|
||||||
|
depth: { enable: false },
|
||||||
|
blend: this.getBlend(),
|
||||||
|
stencil: getMask(mask, maskInside),
|
||||||
|
})
|
||||||
|
.then((model) => {
|
||||||
|
callbackModel([model]);
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.warn(err);
|
||||||
|
callbackModel([]);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
public clearModels() {
|
||||||
|
this.texture?.destroy();
|
||||||
|
this.dataTexture?.destroy();
|
||||||
|
this.layer.off('remapping', this.mapping);
|
||||||
|
}
|
||||||
|
protected registerBuiltinAttributes() {
|
||||||
|
this.styleAttributeService.registerStyleAttribute({
|
||||||
|
name: 'rotate',
|
||||||
|
type: AttributeType.Attribute,
|
||||||
|
descriptor: {
|
||||||
|
name: 'a_Rotate',
|
||||||
|
buffer: {
|
||||||
|
usage: gl.DYNAMIC_DRAW,
|
||||||
|
data: [],
|
||||||
|
type: gl.FLOAT,
|
||||||
|
},
|
||||||
|
size: 1,
|
||||||
|
update: (
|
||||||
|
feature: IEncodeFeature,
|
||||||
|
featureIdx: number,
|
||||||
|
vertex: number[],
|
||||||
|
attributeIdx: number,
|
||||||
|
) => {
|
||||||
|
const { rotate = 0 } = feature;
|
||||||
|
return Array.isArray(rotate) ? [rotate[0]] : [rotate as number];
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
this.styleAttributeService.registerStyleAttribute({
|
||||||
|
name: 'textOffsets',
|
||||||
|
type: AttributeType.Attribute,
|
||||||
|
descriptor: {
|
||||||
|
name: 'a_textOffsets',
|
||||||
|
buffer: {
|
||||||
|
// give the WebGL driver a hint that this buffer may change
|
||||||
|
usage: gl.STATIC_DRAW,
|
||||||
|
data: [],
|
||||||
|
type: gl.FLOAT,
|
||||||
|
},
|
||||||
|
size: 2,
|
||||||
|
update: (
|
||||||
|
feature: IEncodeFeature,
|
||||||
|
featureIdx: number,
|
||||||
|
vertex: number[],
|
||||||
|
attributeIdx: number,
|
||||||
|
) => {
|
||||||
|
return [vertex[5], vertex[6]];
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// point layer size;
|
||||||
|
this.styleAttributeService.registerStyleAttribute({
|
||||||
|
name: 'size',
|
||||||
|
type: AttributeType.Attribute,
|
||||||
|
descriptor: {
|
||||||
|
name: 'a_Size',
|
||||||
|
buffer: {
|
||||||
|
// give the WebGL driver a hint that this buffer may change
|
||||||
|
usage: gl.DYNAMIC_DRAW,
|
||||||
|
data: [],
|
||||||
|
type: gl.FLOAT,
|
||||||
|
},
|
||||||
|
size: 1,
|
||||||
|
update: (
|
||||||
|
feature: IEncodeFeature,
|
||||||
|
featureIdx: number,
|
||||||
|
vertex: number[],
|
||||||
|
attributeIdx: number,
|
||||||
|
) => {
|
||||||
|
const { size = 12 } = feature;
|
||||||
|
return Array.isArray(size) ? [size[0]] : [size as number];
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// point layer size;
|
||||||
|
this.styleAttributeService.registerStyleAttribute({
|
||||||
|
name: 'textUv',
|
||||||
|
type: AttributeType.Attribute,
|
||||||
|
descriptor: {
|
||||||
|
name: 'a_tex',
|
||||||
|
buffer: {
|
||||||
|
// give the WebGL driver a hint that this buffer may change
|
||||||
|
usage: gl.DYNAMIC_DRAW,
|
||||||
|
data: [],
|
||||||
|
type: gl.FLOAT,
|
||||||
|
},
|
||||||
|
size: 2,
|
||||||
|
update: (
|
||||||
|
feature: IEncodeFeature,
|
||||||
|
featureIdx: number,
|
||||||
|
vertex: number[],
|
||||||
|
attributeIdx: number,
|
||||||
|
) => {
|
||||||
|
return [vertex[3], vertex[4]];
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private mapping = () => {
|
||||||
|
this.initGlyph();
|
||||||
|
this.updateTexture();
|
||||||
|
this.filterGlyphs();
|
||||||
|
this.reBuildModel();
|
||||||
|
};
|
||||||
|
private textExtent(): [[number, number], [number, number]] {
|
||||||
|
const bounds = this.mapService.getBounds();
|
||||||
|
return padBounds(bounds, 0.5);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 生成文字纹理(生成文字纹理字典)
|
||||||
|
*/
|
||||||
|
private initTextFont() {
|
||||||
|
const {
|
||||||
|
fontWeight = '400',
|
||||||
|
fontFamily = 'sans-serif',
|
||||||
|
} = this.layer.getLayerConfig() as IPointLayerStyleOptions;
|
||||||
|
const data = this.layer.getEncodedData();
|
||||||
|
const characterSet: string[] = [];
|
||||||
|
data.forEach((item: IEncodeFeature) => {
|
||||||
|
let { shape = '' } = item;
|
||||||
|
shape = shape.toString();
|
||||||
|
for (const char of shape) {
|
||||||
|
// 去重
|
||||||
|
if (characterSet.indexOf(char) === -1) {
|
||||||
|
characterSet.push(char);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.fontService.setFontOptions({
|
||||||
|
characterSet,
|
||||||
|
fontWeight,
|
||||||
|
fontFamily,
|
||||||
|
iconfont: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成 iconfont 纹理字典
|
||||||
|
*/
|
||||||
|
private initIconFontTex() {
|
||||||
|
const {
|
||||||
|
fontWeight = '400',
|
||||||
|
fontFamily = 'sans-serif',
|
||||||
|
} = this.layer.getLayerConfig() as IPointLayerStyleOptions;
|
||||||
|
const data = this.layer.getEncodedData();
|
||||||
|
const characterSet: string[] = [];
|
||||||
|
data.forEach((item: IEncodeFeature) => {
|
||||||
|
let { shape = '' } = item;
|
||||||
|
shape = `${shape}`;
|
||||||
|
if (characterSet.indexOf(shape) === -1) {
|
||||||
|
characterSet.push(shape);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.fontService.setFontOptions({
|
||||||
|
characterSet,
|
||||||
|
fontWeight,
|
||||||
|
fontFamily,
|
||||||
|
iconfont: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成文字布局(对照文字纹理字典提取对应文字的位置很好信息)
|
||||||
|
*/
|
||||||
|
private generateGlyphLayout(iconfont: boolean) {
|
||||||
|
// TODO:更新文字布局
|
||||||
|
const { mapping } = this.fontService;
|
||||||
|
const {
|
||||||
|
spacing = 2,
|
||||||
|
textAnchor = 'center',
|
||||||
|
} = this.layer.getLayerConfig() as IPointLayerStyleOptions;
|
||||||
|
const data = this.layer.getEncodedData();
|
||||||
|
|
||||||
|
this.glyphInfo = data.map((feature: IEncodeFeature) => {
|
||||||
|
const { shape = '', id, size = 1, textOffset = [0, 0] } = feature;
|
||||||
|
|
||||||
|
const shaping = shapeText(
|
||||||
|
shape.toString(),
|
||||||
|
mapping,
|
||||||
|
// @ts-ignore
|
||||||
|
size,
|
||||||
|
textAnchor,
|
||||||
|
'left',
|
||||||
|
spacing,
|
||||||
|
textOffset,
|
||||||
|
iconfont,
|
||||||
|
);
|
||||||
|
const glyphQuads = getGlyphQuads(shaping, textOffset, false);
|
||||||
|
feature.shaping = shaping;
|
||||||
|
feature.glyphQuads = glyphQuads;
|
||||||
|
|
||||||
|
feature.centroid = calculateCentroid(feature.coordinates);
|
||||||
|
|
||||||
|
// 此时地图高德2.0 originCentroid == centroid
|
||||||
|
feature.originCentroid =
|
||||||
|
feature.version === 'GAODE2.x'
|
||||||
|
? calculateCentroid(feature.originCoordinates)
|
||||||
|
: (feature.originCentroid = feature.centroid);
|
||||||
|
|
||||||
|
this.glyphInfoMap[id as number] = {
|
||||||
|
shaping,
|
||||||
|
glyphQuads,
|
||||||
|
centroid: calculateCentroid(feature.coordinates),
|
||||||
|
};
|
||||||
|
return feature;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 文字避让 depend on originCentorid
|
||||||
|
*/
|
||||||
|
private filterGlyphs() {
|
||||||
|
const {
|
||||||
|
padding = [4, 4],
|
||||||
|
textAllowOverlap = false,
|
||||||
|
} = this.layer.getLayerConfig() as IPointLayerStyleOptions;
|
||||||
|
if (textAllowOverlap) {
|
||||||
|
// 如果允许文本覆盖
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.glyphInfoMap = {};
|
||||||
|
this.currentZoom = this.mapService.getZoom();
|
||||||
|
this.extent = this.textExtent();
|
||||||
|
const { width, height } = this.rendererService.getViewportSize();
|
||||||
|
const collisionIndex = new CollisionIndex(width, height);
|
||||||
|
const filterData = this.glyphInfo.filter((feature: IEncodeFeature) => {
|
||||||
|
const { shaping, id = 0 } = feature;
|
||||||
|
const centroid = (feature.version === 'GAODE2.x'
|
||||||
|
? feature.originCentroid
|
||||||
|
: feature.centroid) as [number, number];
|
||||||
|
const size = feature.size as number;
|
||||||
|
const fontScale: number = size / 24;
|
||||||
|
const pixels = this.mapService.lngLatToContainer(centroid);
|
||||||
|
const { box } = collisionIndex.placeCollisionBox({
|
||||||
|
x1: shaping.left * fontScale - padding[0],
|
||||||
|
x2: shaping.right * fontScale + padding[0],
|
||||||
|
y1: shaping.top * fontScale - padding[1],
|
||||||
|
y2: shaping.bottom * fontScale + padding[1],
|
||||||
|
anchorPointX: pixels.x,
|
||||||
|
anchorPointY: pixels.y,
|
||||||
|
});
|
||||||
|
if (box && box.length) {
|
||||||
|
// TODO:featureIndex
|
||||||
|
collisionIndex.insertCollisionBox(box, id);
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
filterData.forEach((item) => {
|
||||||
|
// @ts-ignore
|
||||||
|
this.glyphInfoMap[item.id as number] = item;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 初始化文字布局
|
||||||
|
*/
|
||||||
|
private initGlyph() {
|
||||||
|
const { iconfont = false } = this.layer.getLayerConfig();
|
||||||
|
// 1.生成文字纹理(或是生成 iconfont)
|
||||||
|
iconfont ? this.initIconFontTex() : this.initTextFont();
|
||||||
|
|
||||||
|
// 2.生成文字布局
|
||||||
|
this.generateGlyphLayout(iconfont);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 更新文字纹理
|
||||||
|
*/
|
||||||
|
private updateTexture() {
|
||||||
|
const { createTexture2D } = this.rendererService;
|
||||||
|
const { canvas } = this.fontService;
|
||||||
|
this.textureHeight = canvas.height;
|
||||||
|
if (this.texture) {
|
||||||
|
this.texture.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.texture = createTexture2D({
|
||||||
|
data: canvas,
|
||||||
|
mag: gl.LINEAR,
|
||||||
|
min: gl.LINEAR,
|
||||||
|
width: canvas.width,
|
||||||
|
height: canvas.height,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private reBuildModel() {
|
||||||
|
const {
|
||||||
|
mask = false,
|
||||||
|
maskInside = true,
|
||||||
|
} = this.layer.getLayerConfig() as IPointLayerStyleOptions;
|
||||||
|
this.filterGlyphs();
|
||||||
|
this.layer
|
||||||
|
.buildLayerModel({
|
||||||
|
moduleName: 'pointTileText',
|
||||||
|
vertexShader: textVert,
|
||||||
|
fragmentShader: textFrag,
|
||||||
|
triangulation: TextTriangulation.bind(this),
|
||||||
|
depth: { enable: false },
|
||||||
|
blend: this.getBlend(),
|
||||||
|
stencil: getMask(mask, maskInside),
|
||||||
|
})
|
||||||
|
.then((model) => {
|
||||||
|
this.layer.models = [model];
|
||||||
|
this.layer.renderLayers();
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.warn(err);
|
||||||
|
this.layer.models = [];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,6 +6,14 @@ import { isVectorTile } from '../tile/utils';
|
||||||
|
|
||||||
export default class PolygonLayer extends BaseLayer<IPolygonLayerStyleOptions> {
|
export default class PolygonLayer extends BaseLayer<IPolygonLayerStyleOptions> {
|
||||||
public type: string = 'PolygonLayer';
|
public type: string = 'PolygonLayer';
|
||||||
|
public defaultSourceConfig: {
|
||||||
|
data: [];
|
||||||
|
options: {
|
||||||
|
parser: {
|
||||||
|
type: 'geojson';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
public buildModels() {
|
public buildModels() {
|
||||||
const shape = this.getModelType();
|
const shape = this.getModelType();
|
||||||
this.layerModel = new PolygonModels[shape](this);
|
this.layerModel = new PolygonModels[shape](this);
|
||||||
|
|
|
@ -184,13 +184,14 @@ export class TileLayerManager implements ITileLayerManager {
|
||||||
this.rampColorsData = generateColorRamp(rampColors as IColorRamp);
|
this.rampColorsData = generateColorRamp(rampColors as IColorRamp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
this.initOptions = {
|
this.initOptions = {
|
||||||
layerType: this.parent.type,
|
layerType: this.parent.type,
|
||||||
transforms: this.transforms,
|
transforms: this.transforms,
|
||||||
shape: layerShape,
|
shape: layerShape,
|
||||||
zIndex,
|
zIndex,
|
||||||
opacity,
|
opacity,
|
||||||
sourceLayer: parentParserType === 'geojsonvt' ? 'geojsonvt' : sourceLayer,
|
sourceLayer: this.getSourceLayer(parentParserType, sourceLayer),
|
||||||
coords,
|
coords,
|
||||||
featureId,
|
featureId,
|
||||||
color: colorValue,
|
color: colorValue,
|
||||||
|
@ -216,6 +217,16 @@ export class TileLayerManager implements ITileLayerManager {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private getSourceLayer(parentParserType: string, sourceLayer: string|undefined) {
|
||||||
|
if(parentParserType === 'geojsonvt') {
|
||||||
|
return 'geojsonvt';
|
||||||
|
} else if(parentParserType === 'testTile') {
|
||||||
|
return 'testTile';
|
||||||
|
} else {
|
||||||
|
return sourceLayer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private setConfigListener() {
|
private setConfigListener() {
|
||||||
// RasterLayer PolygonLayer LineLayer PointLayer
|
// RasterLayer PolygonLayer LineLayer PointLayer
|
||||||
// All Tile Layer Need Listen
|
// All Tile Layer Need Listen
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { IModelUniform } from '@antv/l7-core';
|
import { IModelUniform } from '@antv/l7-core';
|
||||||
import BaseModel from '../../core/BaseModel';
|
import BaseModel from '../../core/BaseModel';
|
||||||
import { TMSTileLayer } from '../tmsTileLayer';
|
import { TMSTileLayer } from '../tmsTileLayer';
|
||||||
export default class RasterTileModel extends BaseModel {
|
export default class TileModel extends BaseModel {
|
||||||
public getUninforms(): IModelUniform {
|
public getUninforms(): IModelUniform {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,12 +4,14 @@ import VectorPointLayer from './point';
|
||||||
import VectorPolygonTile from './polygon';
|
import VectorPolygonTile from './polygon';
|
||||||
import RasterTileFactory from './raster';
|
import RasterTileFactory from './raster';
|
||||||
import RasterDataFactory from './rasterData';
|
import RasterDataFactory from './rasterData';
|
||||||
|
import TestTile from './test';
|
||||||
|
|
||||||
export type TileType =
|
export type TileType =
|
||||||
| 'PolygonLayer'
|
| 'PolygonLayer'
|
||||||
| 'PointLayer'
|
| 'PointLayer'
|
||||||
| 'LineLayer'
|
| 'LineLayer'
|
||||||
| 'RasterLayer';
|
| 'RasterLayer'
|
||||||
|
| 'TileDebugLayer';
|
||||||
|
|
||||||
export function getTileFactory(tileType: TileType, parser: IParserCfg) {
|
export function getTileFactory(tileType: TileType, parser: IParserCfg) {
|
||||||
switch (tileType) {
|
switch (tileType) {
|
||||||
|
@ -19,6 +21,8 @@ export function getTileFactory(tileType: TileType, parser: IParserCfg) {
|
||||||
return VectorLineTile;
|
return VectorLineTile;
|
||||||
case 'PointLayer':
|
case 'PointLayer':
|
||||||
return VectorPointLayer;
|
return VectorPointLayer;
|
||||||
|
case 'TileDebugLayer':
|
||||||
|
return TestTile;
|
||||||
case 'RasterLayer':
|
case 'RasterLayer':
|
||||||
if (parser.dataType === 'arraybuffer') {
|
if (parser.dataType === 'arraybuffer') {
|
||||||
return RasterDataFactory;
|
return RasterDataFactory;
|
||||||
|
|
|
@ -0,0 +1,69 @@
|
||||||
|
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';
|
||||||
|
|
||||||
|
export default class TestTile extends TileFactory {
|
||||||
|
public parentLayer: ILayer;
|
||||||
|
|
||||||
|
constructor(option: ITileFactoryOptions) {
|
||||||
|
super(option);
|
||||||
|
this.parentLayer = option.parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public createTile(tile: Tile, initOptions: ISubLayerInitOptions) {
|
||||||
|
const { sourceLayer } = initOptions;
|
||||||
|
if (!sourceLayer) {
|
||||||
|
return {
|
||||||
|
layers: [],
|
||||||
|
layerIDList: [],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
const vectorTileLayer = tile.data.layers[sourceLayer];
|
||||||
|
const features = vectorTileLayer?.features;
|
||||||
|
|
||||||
|
if (features.length === 0) {
|
||||||
|
return {
|
||||||
|
layers: [],
|
||||||
|
layerIDList: [],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const properties = features[0].properties;
|
||||||
|
|
||||||
|
const text = new VectorLayer({ layerType: 'PointLayer' })
|
||||||
|
.source([properties], {
|
||||||
|
parser: {
|
||||||
|
type: 'json',
|
||||||
|
x: 'textLng',
|
||||||
|
y: 'textLat'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.shape('key', 'text')
|
||||||
|
.size(20)
|
||||||
|
.color('#000')
|
||||||
|
.style({
|
||||||
|
stroke: '#fff',
|
||||||
|
strokeWidth: 2
|
||||||
|
})
|
||||||
|
|
||||||
|
const line = new VectorLayer({ layerType: 'LineLayer' })
|
||||||
|
.source({
|
||||||
|
type: 'FeatureCollection',
|
||||||
|
features: features,
|
||||||
|
})
|
||||||
|
.shape('simple')
|
||||||
|
.color('#000')
|
||||||
|
|
||||||
|
registerLayers(this.parentLayer, [line, text]);
|
||||||
|
|
||||||
|
return {
|
||||||
|
layers: [line, text],
|
||||||
|
layerIDList: [line.id, text.id],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
|
@ -8,7 +8,7 @@ import {
|
||||||
import lineFillModel from '../../line/models/tile';
|
import lineFillModel from '../../line/models/tile';
|
||||||
import lineSimpleModel from '../../line/models/simpleLine';
|
import lineSimpleModel from '../../line/models/simpleLine';
|
||||||
|
|
||||||
import pointTextModel from '../../point/models/text';
|
import pointTextModel from '../../point/models/tileText';
|
||||||
import pointFillModel from '../../point/models/tile';
|
import pointFillModel from '../../point/models/tile';
|
||||||
import polygonFillModel from '../../polygon/models/tile';
|
import polygonFillModel from '../../polygon/models/tile';
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
import BaseLayer from '../core/BaseLayer';
|
||||||
|
import { IBaseLayerStyleOptions } from '../core/interface';
|
||||||
|
import TileModel from './models/tileModel';
|
||||||
|
|
||||||
|
export default class TileDebugLayer extends BaseLayer<IBaseLayerStyleOptions> {
|
||||||
|
public type: string = 'TileDebugLayer';
|
||||||
|
public defaultSourceConfig = {
|
||||||
|
data: [],
|
||||||
|
options: {
|
||||||
|
parser: {
|
||||||
|
type: 'testTile',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
public buildModels() {
|
||||||
|
this.layerModel = new TileModel(this);
|
||||||
|
this.layerModel.initModels((models) => {
|
||||||
|
this.models = models;
|
||||||
|
this.renderLayers();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -7,7 +7,7 @@ import {
|
||||||
import { DOM, Tile } from '@antv/l7-utils';
|
import { DOM, Tile } from '@antv/l7-utils';
|
||||||
import { Container } from 'inversify';
|
import { Container } from 'inversify';
|
||||||
|
|
||||||
export const tileVectorParser = ['mvt', 'geojsonvt'];
|
export const tileVectorParser = ['mvt', 'geojsonvt', 'testTile'];
|
||||||
|
|
||||||
export function isVectorTile(parserType: string) {
|
export function isVectorTile(parserType: string) {
|
||||||
return tileVectorParser.indexOf(parserType) >= 0;
|
return tileVectorParser.indexOf(parserType) >= 0;
|
||||||
|
|
|
@ -2,11 +2,12 @@ import { registerParser, registerTransform } from './factory';
|
||||||
import csv from './parser/csv';
|
import csv from './parser/csv';
|
||||||
import geojson from './parser/geojson';
|
import geojson from './parser/geojson';
|
||||||
import image from './parser/image';
|
import image from './parser/image';
|
||||||
import json, { defaultData, defaultParser, defaultSource } from './parser/json';
|
import json from './parser/json';
|
||||||
import mapboxVectorTile from './parser/mvt';
|
import mapboxVectorTile from './parser/mvt';
|
||||||
import geojsonVTTile from './parser/geojsonvt';
|
import geojsonVTTile from './parser/geojsonvt';
|
||||||
import raster from './parser/raster';
|
import raster from './parser/raster';
|
||||||
import rasterTile from './parser/raster-tile';
|
import rasterTile from './parser/raster-tile';
|
||||||
|
import testTile from './parser/testTile';
|
||||||
import Source from './source';
|
import Source from './source';
|
||||||
import { cluster } from './transform/cluster';
|
import { cluster } from './transform/cluster';
|
||||||
import { filter } from './transform/filter';
|
import { filter } from './transform/filter';
|
||||||
|
@ -18,6 +19,7 @@ import { map } from './transform/map';
|
||||||
registerParser('rasterTile', rasterTile);
|
registerParser('rasterTile', rasterTile);
|
||||||
registerParser('mvt', mapboxVectorTile);
|
registerParser('mvt', mapboxVectorTile);
|
||||||
registerParser('geojsonvt', geojsonVTTile);
|
registerParser('geojsonvt', geojsonVTTile);
|
||||||
|
registerParser('testTile', testTile);
|
||||||
registerParser('geojson', geojson);
|
registerParser('geojson', geojson);
|
||||||
registerParser('image', image);
|
registerParser('image', image);
|
||||||
registerParser('csv', csv);
|
registerParser('csv', csv);
|
||||||
|
@ -39,8 +41,4 @@ export {
|
||||||
|
|
||||||
export * from './interface';
|
export * from './interface';
|
||||||
|
|
||||||
export const DEFAULT_SOURCE = defaultSource;
|
|
||||||
export const DEFAULT_DATA = defaultData;
|
|
||||||
export const DEFAULT_PARSER = defaultParser;
|
|
||||||
|
|
||||||
export default Source;
|
export default Source;
|
||||||
|
|
|
@ -84,55 +84,3 @@ export default function json(data: IJsonData, cfg: IParserCfg): IParserData {
|
||||||
dataArray: resultData,
|
dataArray: resultData,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export const defaultSource = {
|
|
||||||
PointLayer: {
|
|
||||||
data: [],
|
|
||||||
options: {
|
|
||||||
parser: {
|
|
||||||
type: 'json',
|
|
||||||
x: 'lng',
|
|
||||||
y: 'lat',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
LineLayer: {
|
|
||||||
data: [
|
|
||||||
{
|
|
||||||
lng1: 100,
|
|
||||||
lat1: 30.0,
|
|
||||||
lng2: 130,
|
|
||||||
lat2: 30,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
options: {
|
|
||||||
parser: {
|
|
||||||
type: 'json',
|
|
||||||
x: 'lng1',
|
|
||||||
y: 'lat1',
|
|
||||||
x1: 'lng2',
|
|
||||||
y1: 'lat2',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
// TODO: 提供默认数据和解析器
|
|
||||||
export const defaultData = [
|
|
||||||
{
|
|
||||||
lng1: 100,
|
|
||||||
lat1: 30.0,
|
|
||||||
lng2: 130,
|
|
||||||
lat2: 30,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
export const defaultParser = {
|
|
||||||
parser: {
|
|
||||||
type: 'json',
|
|
||||||
x: 'lng1',
|
|
||||||
y: 'lat1',
|
|
||||||
x1: 'lng2',
|
|
||||||
y1: 'lat2',
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
|
@ -0,0 +1,76 @@
|
||||||
|
import { Tile, TilesetManagerOptions } from '@antv/l7-utils';
|
||||||
|
import { VectorTileLayer } from '@mapbox/vector-tile';
|
||||||
|
import { Feature } from '@turf/helpers';
|
||||||
|
import { IParserData, ITileParserCFG } from '../interface';
|
||||||
|
|
||||||
|
const DEFAULT_CONFIG: Partial<TilesetManagerOptions> = {
|
||||||
|
tileSize: 256,
|
||||||
|
minZoom: 0,
|
||||||
|
maxZoom: Infinity,
|
||||||
|
zoomOffset: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
export type MapboxVectorTile = {
|
||||||
|
layers: { [_: string]: VectorTileLayer & { features: Feature[] } };
|
||||||
|
};
|
||||||
|
|
||||||
|
const getVectorTile = async (tile: Tile): Promise<MapboxVectorTile> => {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
const [minLng, minLat, maxLng, maxLat] = tile.bounds;
|
||||||
|
// minLng/maxLat ---- maxLng/maxLat
|
||||||
|
// | |
|
||||||
|
// | |
|
||||||
|
// | |
|
||||||
|
// minLng/minLat --- maxLng/minLat
|
||||||
|
|
||||||
|
const vectorTile = {
|
||||||
|
layers: {
|
||||||
|
// Tip: fixed SourceLayer Name
|
||||||
|
testTile: ({
|
||||||
|
features: [
|
||||||
|
{
|
||||||
|
type: 'Feature',
|
||||||
|
properties: {
|
||||||
|
key: tile.x + '/' + tile.y + '/' + tile.z,
|
||||||
|
textLng: (minLng + maxLng) / 2,
|
||||||
|
textLat: (minLat + maxLat) / 2,
|
||||||
|
},
|
||||||
|
geometry: {
|
||||||
|
type: 'LineString',
|
||||||
|
coordinates: [
|
||||||
|
[maxLng, maxLat],
|
||||||
|
[maxLng, minLat],
|
||||||
|
[minLng, minLat],
|
||||||
|
[minLng, minLat],
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
} as unknown) as VectorTileLayer & {
|
||||||
|
features: Feature[];
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} as MapboxVectorTile;
|
||||||
|
|
||||||
|
resolve(vectorTile);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function mapboxVectorTile(
|
||||||
|
data: string | string[],
|
||||||
|
cfg?: ITileParserCFG,
|
||||||
|
): IParserData {
|
||||||
|
const getTileData = (tile: Tile) => getVectorTile(tile);
|
||||||
|
const tilesetOptions = {
|
||||||
|
...DEFAULT_CONFIG,
|
||||||
|
...cfg,
|
||||||
|
getTileData,
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
data,
|
||||||
|
dataArray: [],
|
||||||
|
tilesetOptions,
|
||||||
|
isTile: true,
|
||||||
|
};
|
||||||
|
}
|
Loading…
Reference in New Issue