mirror of https://gitee.com/antv-l7/antv-l7
feat(source render): source transfrom, layer event
This commit is contained in:
parent
a8619ced46
commit
27a09a7a7a
|
@ -78,3 +78,4 @@ git_log.sh
|
|||
node_modules/
|
||||
packages/l7/package_bak.json
|
||||
|
||||
stories/Test
|
||||
|
|
|
@ -47,9 +47,13 @@ const defaultLayerConfig: Partial<ILayerConfig> = {
|
|||
minZoom: 0,
|
||||
maxZoom: 20,
|
||||
visible: true,
|
||||
autoFit: false,
|
||||
zIndex: 0,
|
||||
enableMultiPassRenderer: false,
|
||||
enablePicking: false,
|
||||
pickedFeatureID: -1,
|
||||
enableMultiPassRenderer: true,
|
||||
enablePicking: true,
|
||||
active: false,
|
||||
activeColor: 'red',
|
||||
enableHighlight: false,
|
||||
highlightColor: 'red',
|
||||
enableTAA: false,
|
||||
|
|
|
@ -8,7 +8,7 @@ export interface IInteractionService {
|
|||
destroy(): void;
|
||||
on(
|
||||
eventName: InteractionEvent,
|
||||
callback: (params: { x: number; y: number }) => void,
|
||||
callback: (params: { x: number; y: number; type: string }) => void,
|
||||
): void;
|
||||
triggerHover({ x, y }: { x: number; y: number }): void;
|
||||
triggerHover({ x, y, type }: { x: number; y: number; type?: string }): void;
|
||||
}
|
||||
|
|
|
@ -5,7 +5,6 @@ import { TYPES } from '../../types';
|
|||
import { ILogService } from '../log/ILogService';
|
||||
import { IMapService } from '../map/IMapService';
|
||||
import { IInteractionService, InteractionEvent } from './IInteractionService';
|
||||
|
||||
/**
|
||||
* 由于目前 L7 与地图结合的方案为双 canvas 而非共享 WebGL Context,事件监听注册在地图底图上。
|
||||
* 除此之外,后续如果支持非地图场景,事件监听就需要注册在 L7 canvas 上。
|
||||
|
@ -49,8 +48,13 @@ export default class InteractionService extends EventEmitter
|
|||
// hammertime.on('panmove', this.onPanmove);
|
||||
// hammertime.on('panend', this.onPanend);
|
||||
// hammertime.on('pinch', this.onPinch);
|
||||
|
||||
$containter.addEventListener('mousemove', this.onHover);
|
||||
$containter.addEventListener('click', this.onHover);
|
||||
$containter.addEventListener('mousedown', this.onHover);
|
||||
$containter.addEventListener('mouseup', this.onHover);
|
||||
$containter.addEventListener('dblclick', this.onHover);
|
||||
$containter.addEventListener('contextmenu', this.onHover);
|
||||
|
||||
this.hammertime = hammertime;
|
||||
|
||||
// TODO: 根据场景注册事件到 L7 canvas 上
|
||||
|
@ -62,16 +66,21 @@ export default class InteractionService extends EventEmitter
|
|||
const $containter = this.mapService.getMapContainer();
|
||||
if ($containter) {
|
||||
$containter.removeEventListener('mousemove', this.onHover);
|
||||
$containter.removeEventListener('click', this.onHover);
|
||||
$containter.removeEventListener('mousedown', this.onHover);
|
||||
$containter.removeEventListener('mouseup', this.onHover);
|
||||
$containter.removeEventListener('dblclick', this.onHover);
|
||||
$containter.removeEventListener('contextmenu', this.onHover);
|
||||
}
|
||||
}
|
||||
|
||||
private onHover = ({ x, y }: MouseEvent) => {
|
||||
private onHover = ({ x, y, type }: MouseEvent) => {
|
||||
const $containter = this.mapService.getMapContainer();
|
||||
if ($containter) {
|
||||
const { top, left } = $containter.getBoundingClientRect();
|
||||
x -= left;
|
||||
y -= top;
|
||||
}
|
||||
this.emit(InteractionEvent.Hover, { x, y });
|
||||
this.emit(InteractionEvent.Hover, { x, y, type });
|
||||
};
|
||||
}
|
||||
|
|
|
@ -22,7 +22,13 @@ import {
|
|||
StyleAttributeOption,
|
||||
Triangulation,
|
||||
} from './IStyleAttributeService';
|
||||
|
||||
export interface IDataState {
|
||||
dataSourceNeedUpdate: boolean;
|
||||
dataMappingNeedUpdate: boolean;
|
||||
filterNeedUpdate: boolean;
|
||||
featureScaleNeedUpdate: boolean;
|
||||
StyleAttrNeedUpdate: boolean;
|
||||
}
|
||||
export interface ILayerModelInitializationOptions {
|
||||
moduleName: string;
|
||||
vertexShader: string;
|
||||
|
@ -44,6 +50,10 @@ export interface IPickedFeature {
|
|||
lnglat?: { lng: number; lat: number };
|
||||
feature?: unknown;
|
||||
}
|
||||
// 交互样式
|
||||
export interface IActiveOption {
|
||||
color: string | number[];
|
||||
}
|
||||
|
||||
export interface ILayer {
|
||||
id: string; // 一个场景中同一类型 Layer 可能存在多个
|
||||
|
@ -52,9 +62,11 @@ export interface ILayer {
|
|||
zIndex: number;
|
||||
plugins: ILayerPlugin[];
|
||||
layerModelNeedUpdate: boolean;
|
||||
dataPluginsState: { [key: string]: boolean };
|
||||
dataState: IDataState; // 数据流状态
|
||||
pickedFeatureID: number;
|
||||
hooks: {
|
||||
init: SyncBailHook<void, boolean | void>;
|
||||
afterInit: SyncBailHook<void, boolean | void>;
|
||||
beforeRenderData: SyncWaterfallHook<boolean | void>;
|
||||
beforeRender: SyncBailHook<void, boolean | void>;
|
||||
afterRender: SyncHook<void>;
|
||||
|
@ -87,7 +99,16 @@ export interface ILayer {
|
|||
animate(option: IAnimateOption): ILayer;
|
||||
// pattern(field: string, value: StyleAttributeOption): ILayer;
|
||||
filter(field: string, value: StyleAttributeOption): ILayer;
|
||||
// active(option: ActiveOption): ILayer;
|
||||
active(option: IActiveOption | boolean): ILayer;
|
||||
setActive(
|
||||
id: number | { x: number; y: number },
|
||||
option?: IActiveOption,
|
||||
): void;
|
||||
select(option: IActiveOption | false): ILayer;
|
||||
setSelect(
|
||||
id: number | { x: number; y: number },
|
||||
option?: IActiveOption,
|
||||
): void;
|
||||
style(options: unknown): ILayer;
|
||||
hide(): ILayer;
|
||||
show(): ILayer;
|
||||
|
@ -100,6 +121,7 @@ export interface ILayer {
|
|||
destroy(): void;
|
||||
source(data: any, option?: ISourceCFG): ILayer;
|
||||
setData(data: any, option?: ISourceCFG): ILayer;
|
||||
fitBounds(): ILayer;
|
||||
/**
|
||||
* 向当前图层注册插件
|
||||
* @param plugin 插件实例
|
||||
|
@ -110,11 +132,13 @@ export interface ILayer {
|
|||
setEncodedData(encodedData: IEncodeFeature[]): void;
|
||||
getEncodedData(): IEncodeFeature[];
|
||||
getScaleOptions(): IScaleOptions;
|
||||
|
||||
/**
|
||||
* 事件
|
||||
*/
|
||||
on(type: string, hander: (...args: any[]) => void): void;
|
||||
off(type: string, hander: (...args: any[]) => void): void;
|
||||
emit(type: string, hander: unknown): void;
|
||||
once(type: string, hander: (...args: any[]) => void): void;
|
||||
/**
|
||||
* JSON Schema 用于校验配置项
|
||||
|
@ -125,6 +149,8 @@ export interface ILayer {
|
|||
* 直接调用拾取方法,在非鼠标交互场景中使用
|
||||
*/
|
||||
pick(query: { x: number; y: number }): void;
|
||||
|
||||
updateLayerConfig(configToUpdate: Partial<ILayerConfig | unknown>): void;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -159,6 +185,8 @@ export interface ILayerConfig {
|
|||
maxZoom: number;
|
||||
visible: boolean;
|
||||
zIndex: number;
|
||||
autoFit: boolean;
|
||||
pickedFeatureID: number;
|
||||
enableMultiPassRenderer: boolean;
|
||||
passes: Array<string | [string, { [key: string]: unknown }]>;
|
||||
|
||||
|
@ -174,6 +202,8 @@ export interface ILayerConfig {
|
|||
* 高亮颜色
|
||||
*/
|
||||
highlightColor: string | number[];
|
||||
active: boolean;
|
||||
activeColor: string | number[];
|
||||
/**
|
||||
* 开启 TAA
|
||||
*/
|
||||
|
|
|
@ -112,7 +112,18 @@ export default class PixelPickingPass<
|
|||
* 拾取视口指定坐标属于的要素
|
||||
* TODO:支持区域拾取
|
||||
*/
|
||||
private pickFromPickingFBO = ({ x, y }: { x: number; y: number }) => {
|
||||
private pickFromPickingFBO = ({
|
||||
x,
|
||||
y,
|
||||
type,
|
||||
}: {
|
||||
x: number;
|
||||
y: number;
|
||||
type: string;
|
||||
}) => {
|
||||
if (!this.layer.isVisible()) {
|
||||
return;
|
||||
}
|
||||
const {
|
||||
getViewportSize,
|
||||
readPixels,
|
||||
|
@ -152,9 +163,9 @@ export default class PixelPickingPass<
|
|||
) {
|
||||
this.logger.debug('picked');
|
||||
const pickedFeatureIdx = decodePickingColor(pickedColors);
|
||||
const rawFeature = this.layer.getSource()?.data?.dataArray[
|
||||
pickedFeatureIdx
|
||||
];
|
||||
const rawFeature = this.layer
|
||||
.getSource()
|
||||
.getFeatureById(pickedFeatureIdx);
|
||||
|
||||
if (!rawFeature) {
|
||||
// this.logger.error(
|
||||
|
@ -162,7 +173,7 @@ export default class PixelPickingPass<
|
|||
// );
|
||||
} else {
|
||||
// trigger onHover/Click callback on layer
|
||||
this.triggerHoverOnLayer({ x, y, feature: rawFeature });
|
||||
this.triggerHoverOnLayer({ x, y, type, feature: rawFeature });
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -175,10 +186,12 @@ export default class PixelPickingPass<
|
|||
private triggerHoverOnLayer({
|
||||
x,
|
||||
y,
|
||||
type,
|
||||
feature,
|
||||
}: {
|
||||
x: number;
|
||||
y: number;
|
||||
type: string;
|
||||
feature: unknown;
|
||||
}) {
|
||||
const { onHover, onClick } = this.layer.getLayerConfig();
|
||||
|
@ -196,6 +209,11 @@ export default class PixelPickingPass<
|
|||
feature,
|
||||
});
|
||||
}
|
||||
this.layer.emit(type, {
|
||||
x,
|
||||
y,
|
||||
feature,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -62,6 +62,7 @@ export interface ISource {
|
|||
cluster: boolean;
|
||||
clusterOptions: Partial<IClusterOptions>;
|
||||
updateClusterData(zoom: number): void;
|
||||
getFeatureById(id: number): unknown;
|
||||
}
|
||||
export interface IRasterCfg {
|
||||
extent: [number, number, number, number];
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
import {
|
||||
gl,
|
||||
IActiveOption,
|
||||
IAnimateOption,
|
||||
IDataState,
|
||||
IEncodeFeature,
|
||||
IFontService,
|
||||
IGlobalConfigService,
|
||||
|
@ -44,12 +46,10 @@ import mergeJsonSchemas from 'merge-json-schemas';
|
|||
import { SyncBailHook, SyncHook, SyncWaterfallHook } from 'tapable';
|
||||
import { normalizePasses } from '../plugins/MultiPassRendererPlugin';
|
||||
import baseLayerSchema from './schema';
|
||||
|
||||
/**
|
||||
* 分配 layer id
|
||||
*/
|
||||
let layerIdCounter = 0;
|
||||
const MapEventTypes = ['zoomchange', 'dragend', 'camerachange', 'resize'];
|
||||
|
||||
export default class BaseLayer<ChildLayerStyleOptions = {}> extends EventEmitter
|
||||
implements ILayer {
|
||||
|
@ -61,15 +61,19 @@ export default class BaseLayer<ChildLayerStyleOptions = {}> extends EventEmitter
|
|||
public maxZoom: number;
|
||||
public inited: boolean = false;
|
||||
public layerModelNeedUpdate: boolean = false;
|
||||
public dataPluginsState: { [key: string]: boolean } = {
|
||||
DataSource: false,
|
||||
DataMapping: false,
|
||||
FeatureScale: false,
|
||||
StyleAttr: false,
|
||||
public pickedFeatureID: number = -1;
|
||||
|
||||
public dataState: IDataState = {
|
||||
dataSourceNeedUpdate: false,
|
||||
dataMappingNeedUpdate: false,
|
||||
filterNeedUpdate: false,
|
||||
featureScaleNeedUpdate: false,
|
||||
StyleAttrNeedUpdate: false,
|
||||
};
|
||||
// 生命周期钩子
|
||||
public hooks = {
|
||||
init: new SyncBailHook<void, boolean | void>(),
|
||||
afterInit: new SyncBailHook<void, boolean | void>(),
|
||||
beforeRender: new SyncBailHook<void, boolean | void>(),
|
||||
beforeRenderData: new SyncWaterfallHook<void | boolean>(['data']),
|
||||
afterRender: new SyncHook<void>(),
|
||||
|
@ -140,6 +144,8 @@ export default class BaseLayer<ChildLayerStyleOptions = {}> extends EventEmitter
|
|||
|
||||
private rawConfig: Partial<ILayerConfig & ChildLayerStyleOptions>;
|
||||
|
||||
private needUpdateConfig: Partial<ILayerConfig & ChildLayerStyleOptions>;
|
||||
|
||||
/**
|
||||
* 待更新样式属性,在初始化阶段完成注册
|
||||
*/
|
||||
|
@ -165,11 +171,20 @@ export default class BaseLayer<ChildLayerStyleOptions = {}> extends EventEmitter
|
|||
public updateLayerConfig(
|
||||
configToUpdate: Partial<ILayerConfig | ChildLayerStyleOptions>,
|
||||
) {
|
||||
const sceneId = this.container.get<string>(TYPES.SceneID);
|
||||
this.configService.setLayerConfig(sceneId, this.id, {
|
||||
...this.configService.getLayerConfig(this.id),
|
||||
...configToUpdate,
|
||||
});
|
||||
if (!this.inited) {
|
||||
this.needUpdateConfig = {
|
||||
...this.needUpdateConfig,
|
||||
...configToUpdate,
|
||||
};
|
||||
} else {
|
||||
const sceneId = this.container.get<string>(TYPES.SceneID);
|
||||
this.configService.setLayerConfig(sceneId, this.id, {
|
||||
...this.configService.getLayerConfig(this.id),
|
||||
...this.needUpdateConfig,
|
||||
...configToUpdate,
|
||||
});
|
||||
this.needUpdateConfig = {};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -273,10 +288,11 @@ export default class BaseLayer<ChildLayerStyleOptions = {}> extends EventEmitter
|
|||
|
||||
// 触发 init 生命周期插件
|
||||
this.hooks.init.call();
|
||||
this.inited = true;
|
||||
|
||||
this.hooks.afterInit.call();
|
||||
|
||||
this.buildModels();
|
||||
|
||||
this.inited = true;
|
||||
// 触发初始化完成事件;
|
||||
this.emit('inited');
|
||||
return this;
|
||||
|
@ -311,6 +327,7 @@ export default class BaseLayer<ChildLayerStyleOptions = {}> extends EventEmitter
|
|||
});
|
||||
return this;
|
||||
}
|
||||
// 对mapping后的数据过滤,scale保持不变
|
||||
public filter(
|
||||
field: StyleAttributeField,
|
||||
values?: StyleAttributeOption,
|
||||
|
@ -322,6 +339,7 @@ export default class BaseLayer<ChildLayerStyleOptions = {}> extends EventEmitter
|
|||
attributeValues: values,
|
||||
updateOptions,
|
||||
});
|
||||
this.dataState.dataMappingNeedUpdate = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -418,11 +436,51 @@ export default class BaseLayer<ChildLayerStyleOptions = {}> extends EventEmitter
|
|||
return this;
|
||||
}
|
||||
|
||||
public active(options: IActiveOption) {
|
||||
this.updateLayerConfig({
|
||||
enableHighlight: isObject(options) ? true : options,
|
||||
highlightColor: isObject(options)
|
||||
? options.color
|
||||
: this.getLayerConfig().highlightColor,
|
||||
});
|
||||
return this;
|
||||
}
|
||||
public setActive(
|
||||
id: number | { x: number; y: number },
|
||||
options?: IActiveOption,
|
||||
): void {
|
||||
if (isObject(id)) {
|
||||
const { x = 0, y = 0 } = id;
|
||||
this.updateLayerConfig({
|
||||
highlightColor: isObject(options)
|
||||
? options.color
|
||||
: this.getLayerConfig().highlightColor,
|
||||
});
|
||||
this.pick({ x, y });
|
||||
} else {
|
||||
this.updateLayerConfig({
|
||||
pickedFeatureID: id,
|
||||
highlightColor: isObject(options)
|
||||
? options.color
|
||||
: this.getLayerConfig().highlightColor,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public select(option: IActiveOption | false): ILayer {
|
||||
return this;
|
||||
}
|
||||
|
||||
public setSelect(
|
||||
id: number | { x: number; y: number },
|
||||
options?: IActiveOption,
|
||||
): void {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
public show(): ILayer {
|
||||
this.updateLayerConfig({
|
||||
visible: true,
|
||||
});
|
||||
this.layerService.renderLayers();
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -430,7 +488,6 @@ export default class BaseLayer<ChildLayerStyleOptions = {}> extends EventEmitter
|
|||
this.updateLayerConfig({
|
||||
visible: false,
|
||||
});
|
||||
this.layerService.renderLayers();
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -467,13 +524,20 @@ export default class BaseLayer<ChildLayerStyleOptions = {}> extends EventEmitter
|
|||
/**
|
||||
* zoom to layer Bounds
|
||||
*/
|
||||
public fitBounds(): void {
|
||||
public fitBounds(): ILayer {
|
||||
if (!this.inited) {
|
||||
this.updateLayerConfig({
|
||||
autoFit: true,
|
||||
});
|
||||
return this;
|
||||
}
|
||||
const source = this.getSource();
|
||||
const extent = source.extent;
|
||||
this.mapService.fitBounds([
|
||||
[extent[0], extent[1]],
|
||||
[extent[2], extent[3]],
|
||||
]);
|
||||
return this;
|
||||
}
|
||||
|
||||
public destroy() {
|
||||
|
@ -614,11 +678,6 @@ export default class BaseLayer<ChildLayerStyleOptions = {}> extends EventEmitter
|
|||
};
|
||||
}
|
||||
|
||||
private registerMapEvent() {
|
||||
MapEventTypes.forEach((type) => {
|
||||
this.mapService.on(type, this.layerMapHander.bind(this, type));
|
||||
});
|
||||
}
|
||||
private layerMapHander(type: string, data: any) {
|
||||
this.emit(type, data);
|
||||
}
|
||||
|
|
|
@ -45,29 +45,29 @@ export default class HexagonModel extends BaseModel {
|
|||
}
|
||||
protected registerBuiltinAttributes() {
|
||||
// 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 } = feature;
|
||||
return Array.isArray(size) ? [size[0]] : [size as number];
|
||||
},
|
||||
},
|
||||
});
|
||||
// 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 } = feature;
|
||||
// return Array.isArray(size) ? [size[0]] : [size as number];
|
||||
// },
|
||||
// },
|
||||
// });
|
||||
|
||||
// point layer size;
|
||||
this.styleAttributeService.registerStyleAttribute({
|
||||
|
|
|
@ -13,6 +13,7 @@ import ConfigSchemaValidationPlugin from './plugins/ConfigSchemaValidationPlugin
|
|||
import DataMappingPlugin from './plugins/DataMappingPlugin';
|
||||
import DataSourcePlugin from './plugins/DataSourcePlugin';
|
||||
import FeatureScalePlugin from './plugins/FeatureScalePlugin';
|
||||
import LayerStylePlugin from './plugins/LayerStylePlugin';
|
||||
import LightingPlugin from './plugins/LightingPlugin';
|
||||
import MultiPassRendererPlugin from './plugins/MultiPassRendererPlugin';
|
||||
import PixelPickingPlugin from './plugins/PixelPickingPlugin';
|
||||
|
@ -56,6 +57,15 @@ container
|
|||
.bind<ILayerPlugin>(TYPES.ILayerPlugin)
|
||||
.to(DataMappingPlugin)
|
||||
.inRequestScope();
|
||||
|
||||
/**
|
||||
* 更新地图样式配置项 如active, show, hide
|
||||
*/
|
||||
container
|
||||
.bind<ILayerPlugin>(TYPES.ILayerPlugin)
|
||||
.to(LayerStylePlugin)
|
||||
.inRequestScope();
|
||||
|
||||
/**
|
||||
* 负责属性更新
|
||||
*/
|
||||
|
|
|
@ -27,13 +27,13 @@ export default class DataMappingPlugin implements ILayerPlugin {
|
|||
}: { styleAttributeService: IStyleAttributeService },
|
||||
) {
|
||||
layer.hooks.init.tap('DataMappingPlugin', () => {
|
||||
this.doMaping(layer, { styleAttributeService });
|
||||
this.generateMaping(layer, { styleAttributeService });
|
||||
});
|
||||
|
||||
layer.hooks.beforeRenderData.tap('DataMappingPlugin', (flag) => {
|
||||
if (flag) {
|
||||
layer.dataPluginsState.DataMapping = false;
|
||||
this.doMaping(layer, { styleAttributeService });
|
||||
if (flag || layer.dataState.dataMappingNeedUpdate) {
|
||||
layer.dataState.dataMappingNeedUpdate = false;
|
||||
this.generateMaping(layer, { styleAttributeService });
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
@ -52,7 +52,7 @@ export default class DataMappingPlugin implements ILayerPlugin {
|
|||
}
|
||||
});
|
||||
}
|
||||
private doMaping(
|
||||
private generateMaping(
|
||||
layer: ILayer,
|
||||
{
|
||||
styleAttributeService,
|
||||
|
@ -62,6 +62,7 @@ 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];
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
import { ILayer, ILayerPlugin, IMapService, TYPES } from '@antv/l7-core';
|
||||
import Source from '@antv/l7-source';
|
||||
import { injectable } from 'inversify';
|
||||
import { encodePickingColor, rgb2arr } from '../utils/color';
|
||||
@injectable()
|
||||
export default class LayerStylePlugin implements ILayerPlugin {
|
||||
public apply(layer: ILayer) {
|
||||
layer.hooks.afterInit.tap('LayerStylePlugin', () => {
|
||||
layer.updateLayerConfig({});
|
||||
const { autoFit } = layer.getLayerConfig();
|
||||
if (autoFit) {
|
||||
layer.fitBounds();
|
||||
}
|
||||
});
|
||||
|
||||
layer.hooks.beforeRender.tap('LayerStylePlugin', () => {
|
||||
const {
|
||||
highlightColor = 'red',
|
||||
pickedFeatureID = -1,
|
||||
} = layer.getLayerConfig();
|
||||
layer.models.forEach((model) =>
|
||||
model.addUniforms({
|
||||
u_PickingStage: 2.0,
|
||||
u_PickingColor: encodePickingColor(pickedFeatureID),
|
||||
u_HighlightColor: rgb2arr(highlightColor as string),
|
||||
}),
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
|
@ -8,15 +8,7 @@ import {
|
|||
IStyleAttributeService,
|
||||
} from '@antv/l7-core';
|
||||
import { injectable } from 'inversify';
|
||||
import { rgb2arr } from '../utils/color';
|
||||
|
||||
function encodePickingColor(featureIdx: number): [number, number, number] {
|
||||
return [
|
||||
(featureIdx + 1) & 255,
|
||||
((featureIdx + 1) >> 8) & 255,
|
||||
(((featureIdx + 1) >> 8) >> 8) & 255,
|
||||
];
|
||||
}
|
||||
import { encodePickingColor, rgb2arr } from '../utils/color';
|
||||
|
||||
const PickingStage = {
|
||||
NONE: 0.0,
|
||||
|
@ -50,9 +42,13 @@ export default class PixelPickingPlugin implements ILayerPlugin {
|
|||
},
|
||||
size: 3,
|
||||
// TODO: 固定 feature range 范围内的 pickingColor 都是固定的,可以生成 cache
|
||||
update: (feature: IEncodeFeature, featureIdx: number) =>
|
||||
update: (feature: IEncodeFeature, featureIdx: number) => {
|
||||
// 只有开启拾取才需要 encode
|
||||
enablePicking ? encodePickingColor(featureIdx) : [0, 0, 0],
|
||||
const { id } = feature;
|
||||
return enablePicking && layer.isVisible()
|
||||
? encodePickingColor(id as number)
|
||||
: [0, 0, 0];
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
@ -60,7 +56,7 @@ export default class PixelPickingPlugin implements ILayerPlugin {
|
|||
// if (layer.multiPassRenderer) {
|
||||
layer.hooks.beforePickingEncode.tap('PixelPickingPlugin', () => {
|
||||
const { enablePicking } = layer.getLayerConfig();
|
||||
if (enablePicking) {
|
||||
if (enablePicking && layer.isVisible()) {
|
||||
layer.models.forEach((model) =>
|
||||
model.addUniforms({
|
||||
u_PickingStage: PickingStage.ENCODE,
|
||||
|
@ -71,7 +67,7 @@ export default class PixelPickingPlugin implements ILayerPlugin {
|
|||
|
||||
layer.hooks.afterPickingEncode.tap('PixelPickingPlugin', () => {
|
||||
const { enablePicking } = layer.getLayerConfig();
|
||||
if (enablePicking) {
|
||||
if (enablePicking && layer.isVisible()) {
|
||||
layer.models.forEach((model) =>
|
||||
model.addUniforms({
|
||||
u_PickingStage: PickingStage.NONE,
|
||||
|
|
|
@ -15,6 +15,16 @@ export function rgb2arr(str: string) {
|
|||
return arr;
|
||||
}
|
||||
|
||||
export function encodePickingColor(
|
||||
featureIdx: number,
|
||||
): [number, number, number] {
|
||||
return [
|
||||
(featureIdx + 1) & 255,
|
||||
((featureIdx + 1) >> 8) & 255,
|
||||
(((featureIdx + 1) >> 8) >> 8) & 255,
|
||||
];
|
||||
}
|
||||
|
||||
export function generateColorRamp(colorRamp: IColorRamp): ImageData {
|
||||
const canvas = document.createElement('canvas');
|
||||
const ctx = canvas.getContext('2d') as CanvasRenderingContext2D;
|
||||
|
|
|
@ -6,8 +6,10 @@ import json from './parser/json';
|
|||
import raster from './parser/raster';
|
||||
import Source from './source';
|
||||
import { cluster } from './transform/cluster';
|
||||
import { filter } from './transform/filter';
|
||||
import { aggregatorToGrid } from './transform/grid';
|
||||
import { pointToHexbin } from './transform/hexagon';
|
||||
import { map } from './transform/map';
|
||||
export default Source;
|
||||
registerParser('geojson', geojson);
|
||||
registerParser('image', image);
|
||||
|
@ -15,6 +17,8 @@ registerParser('csv', csv);
|
|||
registerParser('json', json);
|
||||
registerParser('raster', raster);
|
||||
registerTransform('cluster', cluster);
|
||||
registerTransform('filter', filter);
|
||||
registerTransform('map', map);
|
||||
registerTransform('grid', aggregatorToGrid);
|
||||
registerTransform('hexagon', pointToHexbin);
|
||||
export {
|
||||
|
|
|
@ -121,6 +121,16 @@ export default class Source extends EventEmitter {
|
|||
});
|
||||
this.executeTrans();
|
||||
}
|
||||
public getFeatureById(id: number): unknown {
|
||||
const { type = 'geojson' } = this.parser;
|
||||
if (type === 'geojson') {
|
||||
return id < this.rawData.features.length
|
||||
? this.rawData.features[id]
|
||||
: 'null';
|
||||
} else {
|
||||
return id < this.data.dataArray.length ? this.data.dataArray[id] : 'null';
|
||||
}
|
||||
}
|
||||
|
||||
private excuteParser(): void {
|
||||
const parser = this.parser;
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
import { IParserData } from '@antv/l7-core';
|
||||
export function filter(data: IParserData, options: { [key: string]: any }) {
|
||||
const { callback } = options;
|
||||
if (callback) {
|
||||
data.dataArray = data.dataArray.filter(callback);
|
||||
}
|
||||
return data;
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
import { IParserData } from '@antv/l7-core';
|
||||
type CallBack = (...args: any[]) => any;
|
||||
export function map(data: IParserData, options: { [key: string]: any }) {
|
||||
const { callback } = options;
|
||||
if (callback) {
|
||||
data.dataArray = data.dataArray.map(callback);
|
||||
}
|
||||
return data;
|
||||
}
|
|
@ -5,7 +5,6 @@ import ArcLineDemo from './components/Arcline';
|
|||
import Column from './components/column';
|
||||
import DataUpdate from './components/data_update';
|
||||
import HeatMapDemo from './components/HeatMap';
|
||||
import GridHeatMap from './components/HeatmapGrid';
|
||||
import LineLayer from './components/Line';
|
||||
import PointDemo from './components/Point';
|
||||
import Point3D from './components/Point3D';
|
||||
|
@ -25,7 +24,6 @@ storiesOf('图层', module)
|
|||
.add('线图层', () => <LineLayer />)
|
||||
.add('3D弧线', () => <ArcLineDemo />)
|
||||
.add('2D弧线', () => <Arc2DLineDemo />)
|
||||
.add('网格热力图', () => <GridHeatMap />)
|
||||
.add('热力图', () => <HeatMapDemo />)
|
||||
.add('栅格', () => <RasterLayerDemo />)
|
||||
.add('图片', () => <ImageLayerDemo />);
|
||||
|
|
|
@ -1,85 +0,0 @@
|
|||
import { HeatmapLayer, Scene } from '@antv/l7';
|
||||
import { GaodeMap } from '@antv/l7-maps';
|
||||
import * as React from 'react';
|
||||
|
||||
export default class GridHeatMap extends React.Component {
|
||||
// @ts-ignore
|
||||
private scene: Scene;
|
||||
|
||||
public componentWillUnmount() {
|
||||
this.scene.destroy();
|
||||
}
|
||||
|
||||
public async componentDidMount() {
|
||||
const response = await fetch(
|
||||
'https://gw.alipayobjects.com/os/basement_prod/7359a5e9-3c5e-453f-b207-bc892fb23b84.csv',
|
||||
);
|
||||
const data = await response.text();
|
||||
const scene = new Scene({
|
||||
id: 'map',
|
||||
map: new GaodeMap({
|
||||
style: 'dark',
|
||||
pitch: 0,
|
||||
center: [110.097892, 33.853662],
|
||||
zoom: 4.056,
|
||||
}),
|
||||
});
|
||||
const layer = new HeatmapLayer({
|
||||
enablePicking: true,
|
||||
enableHighlight: true,
|
||||
})
|
||||
.source(data, {
|
||||
parser: {
|
||||
type: 'csv',
|
||||
x: 'lng',
|
||||
y: 'lat',
|
||||
},
|
||||
transforms: [
|
||||
{
|
||||
type: 'grid',
|
||||
size: 10000,
|
||||
field: 'v',
|
||||
method: 'sum',
|
||||
},
|
||||
],
|
||||
})
|
||||
.size('count', (value) => {
|
||||
return value * 0;
|
||||
})
|
||||
.shape('square')
|
||||
.style({
|
||||
coverage: 1,
|
||||
angle: 0,
|
||||
})
|
||||
.color(
|
||||
'count',
|
||||
[
|
||||
'#FF3417',
|
||||
'#FF7412',
|
||||
'#FFB02A',
|
||||
'#FFE754',
|
||||
'#46F3FF',
|
||||
'#02BEFF',
|
||||
'#1A7AFF',
|
||||
'#0A1FB2',
|
||||
].reverse(),
|
||||
);
|
||||
scene.addLayer(layer);
|
||||
this.scene = scene;
|
||||
}
|
||||
|
||||
public render() {
|
||||
return (
|
||||
<div
|
||||
id="map"
|
||||
style={{
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
|
@ -38,13 +38,18 @@ export default class AdvancedAPI extends React.Component {
|
|||
highlightColor: [0, 0, 1, 1],
|
||||
onHover: (pickedFeature) => {
|
||||
// tslint:disable-next-line:no-console
|
||||
console.log(pickedFeature);
|
||||
},
|
||||
onClick: (pickedFeature) => {
|
||||
// tslint:disable-next-line:no-console
|
||||
},
|
||||
});
|
||||
|
||||
layer
|
||||
.source(await response.json())
|
||||
.size('name', [0, 10000, 50000, 30000, 100000])
|
||||
.active({
|
||||
color: 'red',
|
||||
})
|
||||
.color('name', [
|
||||
'#2E8AE6',
|
||||
'#69D1AB',
|
||||
|
@ -58,6 +63,9 @@ export default class AdvancedAPI extends React.Component {
|
|||
opacity: 0.8,
|
||||
});
|
||||
scene.addLayer(layer);
|
||||
layer.on('click', (e) => {
|
||||
console.log(e);
|
||||
});
|
||||
|
||||
this.scene = scene;
|
||||
|
||||
|
@ -67,9 +75,10 @@ export default class AdvancedAPI extends React.Component {
|
|||
const styleOptions = {
|
||||
enablePicking: true,
|
||||
enableHighlight: true,
|
||||
highlightColor: [0, 0, 255],
|
||||
highlightColor: [1, 0, 0],
|
||||
pickingX: window.innerWidth / 2,
|
||||
pickingY: window.innerHeight / 2,
|
||||
visible: true,
|
||||
};
|
||||
const pointFolder = gui.addFolder('非鼠标 hover 交互');
|
||||
pointFolder
|
||||
|
@ -80,23 +89,30 @@ export default class AdvancedAPI extends React.Component {
|
|||
});
|
||||
scene.render();
|
||||
});
|
||||
pointFolder.add(styleOptions, 'visible').onChange((visible: boolean) => {
|
||||
layer.style({
|
||||
visible,
|
||||
});
|
||||
scene.render();
|
||||
});
|
||||
pointFolder
|
||||
.add(styleOptions, 'pickingX', 0, window.innerWidth)
|
||||
.onChange((pickingX: number) => {
|
||||
layer.pick({ x: pickingX, y: styleOptions.pickingY });
|
||||
layer.setActive({ x: pickingX, y: styleOptions.pickingY });
|
||||
});
|
||||
pointFolder
|
||||
.add(styleOptions, 'pickingY', 0, window.innerHeight)
|
||||
.onChange((pickingY: number) => {
|
||||
layer.pick({ x: styleOptions.pickingX, y: pickingY });
|
||||
layer.setActive({ x: styleOptions.pickingX, y: pickingY });
|
||||
});
|
||||
pointFolder
|
||||
.addColor(styleOptions, 'highlightColor')
|
||||
.onChange((highlightColor: number[]) => {
|
||||
const [r, g, b] = highlightColor.map((c) => c / 255);
|
||||
layer.style({
|
||||
highlightColor: [r, g, b, 1],
|
||||
});
|
||||
layer.setActive(
|
||||
{ x: styleOptions.pickingX, y: styleOptions.pickingY },
|
||||
{ color: [r, g, b, 1] },
|
||||
);
|
||||
scene.render();
|
||||
});
|
||||
pointFolder.open();
|
||||
|
|
Loading…
Reference in New Issue