fix: layer filter update

This commit is contained in:
thinkinggis 2020-02-14 12:08:28 +08:00
parent 3df70e6647
commit b39a32f9e9
8 changed files with 122 additions and 43 deletions

View File

@ -69,11 +69,9 @@ export default class LayerService implements ILayerService {
}
public renderLayers() {
// TODO脏检查只渲染发生改变的 Layer
if (this.alreadyInRendering) {
return;
}
//
this.alreadyInRendering = true;
this.clear();
this.updateRenderOrder();

View File

@ -125,7 +125,6 @@ export default class StyleAttributeService implements IStyleAttributeService {
const { elements, sizePerElement } = this.featureLayout;
// 截取待更新的 feature 范围
const featuresToUpdate = elements.slice(startFeatureIdx, endFeatureIdx);
// [n, n] 中断更新
if (!featuresToUpdate.length) {
return;
@ -184,6 +183,11 @@ export default class StyleAttributeService implements IStyleAttributeService {
};
elements: IElements;
} {
// 每次创建的初始化化 LayerOut
this.featureLayout = {
sizePerElement: 0,
elements: [],
};
if (triangulation) {
this.triangulation = triangulation;
}
@ -196,7 +200,6 @@ export default class StyleAttributeService implements IStyleAttributeService {
const indices: number[] = [];
const normals: number[] = [];
let size = 3;
features.forEach((feature, featureIdx) => {
// 逐 feature 进行三角化
const {

View File

@ -360,7 +360,9 @@ export default class BaseLayer<ChildLayerStyleOptions = {}> extends EventEmitter
updateOptions?: Partial<IStyleAttributeUpdateOptions>,
) {
this.updateStyleAttribute('filter', field, values, updateOptions);
this.dataState.dataMappingNeedUpdate = true;
// if (this.inited) {
// this.layerModelNeedUpdate = true;
// }
return this;
}
@ -811,6 +813,7 @@ export default class BaseLayer<ChildLayerStyleOptions = {}> extends EventEmitter
protected renderModels() {
if (this.layerModelNeedUpdate) {
this.models = this.layerModel.buildModels();
this.hooks.beforeRender.call();
this.layerModelNeedUpdate = false;
}
this.models.forEach((model) => {

View File

@ -27,11 +27,16 @@ export default class DataMappingPlugin implements ILayerPlugin {
}: { styleAttributeService: IStyleAttributeService },
) {
layer.hooks.init.tap('DataMappingPlugin', () => {
// 初始化重新生成 map
this.generateMaping(layer, { styleAttributeService });
});
layer.hooks.beforeRenderData.tap('DataMappingPlugin', (flag) => {
if (flag || layer.dataState.dataMappingNeedUpdate) {
if (
flag ||
layer.dataState.dataMappingNeedUpdate ||
layer.layerModelNeedUpdate
) {
layer.dataState.dataMappingNeedUpdate = false;
this.generateMaping(layer, { styleAttributeService });
return true;
@ -42,16 +47,31 @@ export default class DataMappingPlugin implements ILayerPlugin {
// remapping before render
layer.hooks.beforeRender.tap('DataMappingPlugin', () => {
const attributes = styleAttributeService.getLayerStyleAttributes() || [];
const filter = styleAttributeService.getLayerStyleAttribute('filter');
const { dataArray } = layer.getSource().data;
const attributesToRemapping = attributes.filter(
(attribute) => attribute.needRemapping,
(attribute) => attribute.needRemapping, // 如果filter变化
);
let filterData = dataArray;
// 数据过滤完 再执行数据映射
if (filter?.needRemapping && filter?.scale) {
filterData = dataArray.filter((record: IParseDataItem) => {
return this.applyAttributeMapping(filter, record)[0];
});
}
if (attributesToRemapping.length) {
layer.setEncodedData(this.mapping(attributesToRemapping, dataArray));
// 过滤数据
if (filter?.needRemapping) {
layer.setEncodedData(this.mapping(attributes, filterData));
} else {
layer.setEncodedData(this.mapping(attributesToRemapping, filterData));
}
this.logger.debug('remapping finished');
}
});
}
private generateMaping(
layer: ILayer,
{
@ -62,17 +82,16 @@ export default class DataMappingPlugin implements ILayerPlugin {
const filter = styleAttributeService.getLayerStyleAttribute('filter');
const { dataArray } = layer.getSource().data;
let filterData = dataArray;
// 数据过滤完 执行数据映射
// 数据过滤完 执行数据映射
if (filter?.scale) {
filterData = dataArray.filter((record: IParseDataItem) => {
return this.applyAttributeMapping(filter, record)[0];
});
}
// TODO: FIXME
if (!filterData) {
return;
}
// if (!filterData) {
// return;
// }
// mapping with source data
layer.setEncodedData(this.mapping(attributes, filterData));
}

View File

@ -19,6 +19,7 @@ export default class LayerModelPlugin implements ILayerPlugin {
layer.prepareBuildModel();
// 初始化 Model
layer.buildModels();
layer.layerModelNeedUpdate = false;
}
return false;
});

View File

@ -37,10 +37,12 @@ export default class UpdateStyleAttributePlugin implements ILayerPlugin {
});
layer.hooks.beforeRender.tap('UpdateStyleAttributePlugin', () => {
if (layer.layerModelNeedUpdate) {
return;
}
this.updateStyleAtrribute(layer, { styleAttributeService });
});
}
private updateStyleAtrribute(
layer: ILayer,
{
@ -48,10 +50,16 @@ export default class UpdateStyleAttributePlugin implements ILayerPlugin {
}: { styleAttributeService: IStyleAttributeService },
) {
const attributes = styleAttributeService.getLayerStyleAttributes() || [];
const filter = styleAttributeService.getLayerStyleAttribute('filter');
if (filter && filter.needRegenerateVertices) {
layer.layerModelNeedUpdate = true;
attributes.forEach((attr) => (attr.needRegenerateVertices = false));
return;
}
attributes
.filter((attribute) => attribute.needRegenerateVertices)
.forEach((attribute) => {
// 精确更新某个/某些 feature(s),需要传入 featureIdx
// 精确更新某个/某些 feature(s),需要传入 featureIdx d
styleAttributeService.updateAttributeByFeatureRange(
attribute.name,
layer.getEncodedData(), // 获取经过 mapping 最新的数据

View File

@ -9,7 +9,7 @@ import {
IModelUniform,
ITexture2D,
} from '@antv/l7-core';
import { rgb2arr } from '@antv/l7-utils';
import { boundsContains, padBounds, rgb2arr } from '@antv/l7-utils';
import BaseModel from '../../core/BaseModel';
import CollisionIndex from '../../utils/collision-index';
import { calculteCentroid } from '../../utils/geo';
@ -84,18 +84,26 @@ export default class TextModel extends BaseModel {
private glyphInfo: IEncodeFeature[];
private currentZoom: number = -1;
private extent: [[number, number], [number, number]];
private textureHeight: number = 0;
private preTextStyle: Partial<IPointTextLayerStyleOptions> = {};
public getUninforms(): IModelUniform {
const {
fontWeight = 800,
fontFamily = 'sans-serif',
opacity = 1.0,
stroke = '#fff',
strokeWidth = 0,
strokeOpacity = 1,
textAnchor = 'center',
textAllowOverlap = true,
} = this.layer.getLayerConfig() as IPointTextLayerStyleOptions;
this.updateTexture();
const { canvas } = this.fontService;
if (canvas.height !== this.textureHeight) {
this.updateTexture();
}
this.preTextStyle = {
textAnchor,
textAllowOverlap,
};
return {
u_opacity: opacity,
u_stroke_opacity: strokeOpacity,
@ -109,6 +117,14 @@ export default class TextModel extends BaseModel {
public buildModels(): IModel[] {
this.extent = this.textExtent();
const {
textAnchor = 'center',
textAllowOverlap = true,
} = this.layer.getLayerConfig() as IPointTextLayerStyleOptions;
this.preTextStyle = {
textAnchor,
textAllowOverlap,
};
this.initGlyph();
this.updateTexture();
this.filterGlyphs();
@ -127,15 +143,15 @@ export default class TextModel extends BaseModel {
const {
textAllowOverlap = false,
} = this.layer.getLayerConfig() as IPointTextLayerStyleOptions;
// textAllowOverlap 发生改变
const zoom = this.mapService.getZoom();
const extent = this.mapService.getBounds();
const flag =
extent[0][0] < this.extent[0][0] ||
extent[0][1] < this.extent[0][1] ||
extent[1][0] > this.extent[1][0] ||
extent[1][1] < this.extent[1][1];
if (!textAllowOverlap && (Math.abs(this.currentZoom - zoom) > 1 || flag)) {
const flag = boundsContains(this.extent, extent);
// 文本不能压盖则进行过滤
if (
(!textAllowOverlap && (Math.abs(this.currentZoom - zoom) > 1 || !flag)) ||
textAllowOverlap !== this.preTextStyle.textAllowOverlap
) {
this.filterGlyphs();
this.layer.models = [
this.layer.buildLayerModel({
@ -227,12 +243,7 @@ export default class TextModel extends BaseModel {
}
private textExtent(): [[number, number], [number, number]] {
const bounds = this.mapService.getBounds();
const step =
Math.min(bounds[1][0] - bounds[0][0], bounds[1][1] - bounds[1][0]) / 2;
return [
[bounds[0][0] - step, bounds[0][1] - step],
[bounds[1][0] + step, bounds[1][1] + step],
];
return padBounds(bounds, 0.5);
}
/**
*
@ -240,7 +251,7 @@ export default class TextModel extends BaseModel {
private initTextFont() {
const {
fontWeight = '800',
fontFamily,
fontFamily = 'sans-serif',
} = this.layer.getLayerConfig() as IPointTextLayerStyleOptions;
const data = this.layer.getEncodedData();
const characterSet: string[] = [];
@ -264,6 +275,7 @@ export default class TextModel extends BaseModel {
*
*/
private generateGlyphLayout() {
// TODO:更新文字布局
const { mapping } = this.fontService;
const {
spacing = 2,
@ -298,6 +310,7 @@ export default class TextModel extends BaseModel {
textAllowOverlap = false,
} = this.layer.getLayerConfig() as IPointTextLayerStyleOptions;
if (textAllowOverlap) {
this.layer.setEncodedData(this.glyphInfo);
return;
}
this.currentZoom = this.mapService.getZoom();
@ -343,10 +356,26 @@ export default class TextModel extends BaseModel {
private updateTexture() {
const { createTexture2D } = this.rendererService;
const { canvas } = this.fontService;
this.textureHeight = canvas.height;
this.texture = createTexture2D({
data: canvas,
width: canvas.width,
height: canvas.height,
});
}
private rebuildModel() {
// 避让 anchor,等属性变化时需要重新构建model
this.filterGlyphs();
return [
this.layer.buildLayerModel({
moduleName: 'pointText',
vertexShader: textVert,
fragmentShader: textFrag,
triangulation: TextTriangulation,
depth: { enable: false },
blend: this.getBlend(),
}),
];
}
}

View File

@ -1,7 +1,7 @@
import { PointLayer, Scene } from '@antv/l7';
import { GaodeMap, Mapbox } from '@antv/l7-maps';
import * as React from 'react';
import * as dat from 'dat.gui';
import * as React from 'react';
// @ts-ignore
import data from '../data/data.json';
export default class TextLayerDemo extends React.Component {
@ -39,11 +39,15 @@ export default class TextLayerDemo extends React.Component {
y: 'w',
},
})
.shape('m', 'text')
// .shape('m', 'text')
.shape('circle')
.size(12)
.color('#fff')
.filter('t', (t) => {
return t > 14;
})
.color('red')
.style({
textAllowOverlap: true,
// textAllowOverlap: true,
// fontWeight: 200,
// textAnchor: 'center', // 文本相对锚点的位置 center|left|right|top|bottom|top-left
// textOffset: [0, 0], // 文本相对锚点的偏移量 [水平, 垂直]
@ -54,10 +58,6 @@ export default class TextLayerDemo extends React.Component {
// strokeOpacity: 1.0,
});
scene.addLayer(pointLayer);
pointLayer.on('click', (e) => {
console.log(e);
});
this.scene = scene;
const gui = new dat.GUI();
@ -65,7 +65,9 @@ export default class TextLayerDemo extends React.Component {
const styleOptions = {
textAnchor: 'center',
strokeWidth: 1,
textAllowOverlap: false,
opacity: 1,
color: '#ffffff',
};
const rasterFolder = gui.addFolder('文本可视化');
rasterFolder
@ -88,10 +90,19 @@ export default class TextLayerDemo extends React.Component {
});
rasterFolder
.add(styleOptions, 'strokeWidth', 0, 10)
.add(styleOptions, 'strokeWidth', 0, 1000)
.onChange((strokeWidth: number) => {
// pointLayer.filter('t', (t: number) => {
// return t > strokeWidth;
// });
pointLayer.setData(pointsData.list.slice(0, strokeWidth));
scene.render();
});
rasterFolder
.add(styleOptions, 'textAllowOverlap', 0, 10)
.onChange((textAllowOverlap: boolean) => {
pointLayer.style({
strokeWidth,
textAllowOverlap,
});
scene.render();
});
@ -102,7 +113,14 @@ export default class TextLayerDemo extends React.Component {
opacity,
});
scene.render();
setTimeout(() => {
scene.render();
}, 10);
});
rasterFolder.addColor(styleOptions, 'color').onChange((color: string) => {
pointLayer.color(color);
scene.render();
});
// });
}