mirror of https://gitee.com/antv-l7/antv-l7
feat: 增加根据范围选择
This commit is contained in:
parent
f39f4c62bb
commit
a2ec206985
|
@ -46,6 +46,7 @@ export * from './services/component/IMarkerService';
|
||||||
export * from './services/component/IPopupService';
|
export * from './services/component/IPopupService';
|
||||||
export * from './services/log/ILogService';
|
export * from './services/log/ILogService';
|
||||||
export * from './services/interaction/IInteractionService';
|
export * from './services/interaction/IInteractionService';
|
||||||
|
export * from './services/interaction/IPickingService';
|
||||||
|
|
||||||
/** 全部渲染服务接口 */
|
/** 全部渲染服务接口 */
|
||||||
export * from './services/renderer/IAttribute';
|
export * from './services/renderer/IAttribute';
|
||||||
|
|
|
@ -1,3 +1,10 @@
|
||||||
|
import { ILayer } from '../layer/ILayerService';
|
||||||
export interface IPickingService {
|
export interface IPickingService {
|
||||||
init(id: string): void;
|
init(id: string): void;
|
||||||
|
pickBox(layer: ILayer, box: [number, number, number, number]): any[];
|
||||||
|
boxPickLayer(
|
||||||
|
layer: ILayer,
|
||||||
|
box: [number, number, number, number],
|
||||||
|
cb: (...args: any[]) => void,
|
||||||
|
): Promise<any>;
|
||||||
}
|
}
|
||||||
|
|
|
@ -72,6 +72,75 @@ export default class PickingService implements IPickingService {
|
||||||
this.pickingAllLayer.bind(this),
|
this.pickingAllLayer.bind(this),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async boxPickLayer(
|
||||||
|
layer: ILayer,
|
||||||
|
box: [number, number, number, number],
|
||||||
|
cb: (...args: any[]) => void
|
||||||
|
): Promise<any> {
|
||||||
|
const { useFramebuffer, clear, getContainer } = this.rendererService;
|
||||||
|
this.resizePickingFBO();
|
||||||
|
useFramebuffer(this.pickingFBO, () => {
|
||||||
|
clear({
|
||||||
|
framebuffer: this.pickingFBO,
|
||||||
|
color: [0, 0, 0, 0],
|
||||||
|
stencil: 0,
|
||||||
|
depth: 1,
|
||||||
|
});
|
||||||
|
layer.hooks.beforePickingEncode.call();
|
||||||
|
layer.renderModels();
|
||||||
|
layer.hooks.afterPickingEncode.call();
|
||||||
|
const features = this.pickBox(layer, box);
|
||||||
|
cb(features);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public pickBox(layer: ILayer, box: [number, number, number, number]): any[] {
|
||||||
|
const [xMin, yMin, xMax, yMax] = box.map((v) => {
|
||||||
|
const tmpV = v < 0 ? 0 : v;
|
||||||
|
return Math.floor((tmpV * DOM.DPR) / this.pickBufferScale);
|
||||||
|
});
|
||||||
|
const { getViewportSize, readPixels, getContainer } = this.rendererService;
|
||||||
|
let {
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
} = (getContainer() as HTMLElement).getBoundingClientRect();
|
||||||
|
width *= DOM.DPR;
|
||||||
|
height *= DOM.DPR;
|
||||||
|
if (
|
||||||
|
xMin > ((width - 1) * DOM.DPR) / this.pickBufferScale ||
|
||||||
|
xMax < 0 ||
|
||||||
|
yMin > ((height - 1) * DOM.DPR) / this.pickBufferScale ||
|
||||||
|
yMax < 0
|
||||||
|
) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
let pickedColors: Uint8Array | undefined;
|
||||||
|
const w = Math.min(width / this.pickBufferScale, xMax) - xMin;
|
||||||
|
const h = Math.min(height / this.pickBufferScale, yMax) - yMin;
|
||||||
|
pickedColors = readPixels({
|
||||||
|
x: xMin,
|
||||||
|
// 视口坐标系原点在左上,而 WebGL 在左下,需要翻转 Y 轴
|
||||||
|
y: Math.floor(height / this.pickBufferScale - (yMax + 1)),
|
||||||
|
width: w,
|
||||||
|
height: h,
|
||||||
|
data: new Uint8Array(w * h * 4),
|
||||||
|
framebuffer: this.pickingFBO,
|
||||||
|
});
|
||||||
|
|
||||||
|
const features = [];
|
||||||
|
const featuresIdMap: { [key: string]: boolean } = {};
|
||||||
|
for (let i = 0; i < pickedColors.length / 4; i = i + 1) {
|
||||||
|
const color = pickedColors.slice(i * 4, i * 4 + 4);
|
||||||
|
const pickedFeatureIdx = decodePickingColor(color);
|
||||||
|
if (pickedFeatureIdx !== -1 && !featuresIdMap[pickedFeatureIdx]) {
|
||||||
|
const rawFeature = layer.getSource().getFeatureById(pickedFeatureIdx);
|
||||||
|
features.push(rawFeature);
|
||||||
|
featuresIdMap[pickedFeatureIdx] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return features;
|
||||||
|
}
|
||||||
private async pickingAllLayer(target: IInteractionTarget) {
|
private async pickingAllLayer(target: IInteractionTarget) {
|
||||||
if (this.alreadyInPicking || this.layerService.alreadyInRendering) {
|
if (this.alreadyInPicking || this.layerService.alreadyInRendering) {
|
||||||
return;
|
return;
|
||||||
|
@ -81,13 +150,9 @@ export default class PickingService implements IPickingService {
|
||||||
this.layerService.renderLayers();
|
this.layerService.renderLayers();
|
||||||
this.alreadyInPicking = false;
|
this.alreadyInPicking = false;
|
||||||
}
|
}
|
||||||
private async pickingLayers(target: IInteractionTarget) {
|
|
||||||
const {
|
private resizePickingFBO() {
|
||||||
getViewportSize,
|
const { getContainer } = this.rendererService;
|
||||||
useFramebuffer,
|
|
||||||
clear,
|
|
||||||
getContainer,
|
|
||||||
} = this.rendererService;
|
|
||||||
let {
|
let {
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
|
@ -102,6 +167,16 @@ export default class PickingService implements IPickingService {
|
||||||
this.width = width;
|
this.width = width;
|
||||||
this.height = height;
|
this.height = height;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
private async pickingLayers(target: IInteractionTarget) {
|
||||||
|
const {
|
||||||
|
getViewportSize,
|
||||||
|
useFramebuffer,
|
||||||
|
clear,
|
||||||
|
getContainer,
|
||||||
|
} = this.rendererService;
|
||||||
|
|
||||||
|
this.resizePickingFBO();
|
||||||
|
|
||||||
useFramebuffer(this.pickingFBO, () => {
|
useFramebuffer(this.pickingFBO, () => {
|
||||||
const layers = this.layerService.getLayers();
|
const layers = this.layerService.getLayers();
|
||||||
|
@ -123,6 +198,7 @@ export default class PickingService implements IPickingService {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private pickFromPickingFBO = (
|
private pickFromPickingFBO = (
|
||||||
layer: ILayer,
|
layer: ILayer,
|
||||||
{ x, y, lngLat, type, target }: IInteractionTarget,
|
{ x, y, lngLat, type, target }: IInteractionTarget,
|
||||||
|
|
|
@ -192,6 +192,10 @@ export interface ILayer {
|
||||||
* 直接调用拾取方法,在非鼠标交互场景中使用
|
* 直接调用拾取方法,在非鼠标交互场景中使用
|
||||||
*/
|
*/
|
||||||
pick(query: { x: number; y: number }): void;
|
pick(query: { x: number; y: number }): void;
|
||||||
|
boxSelect(
|
||||||
|
box: [number, number, number, number],
|
||||||
|
cb: (...args: any[]) => void,
|
||||||
|
): void;
|
||||||
|
|
||||||
updateLayerConfig(configToUpdate: Partial<ILayerConfig | unknown>): void;
|
updateLayerConfig(configToUpdate: Partial<ILayerConfig | unknown>): void;
|
||||||
setAnimateStartTime(): void;
|
setAnimateStartTime(): void;
|
||||||
|
|
|
@ -25,6 +25,7 @@ import {
|
||||||
IModelInitializationOptions,
|
IModelInitializationOptions,
|
||||||
IMultiPassRenderer,
|
IMultiPassRenderer,
|
||||||
IPass,
|
IPass,
|
||||||
|
IPickingService,
|
||||||
IPostProcessingPass,
|
IPostProcessingPass,
|
||||||
IRendererService,
|
IRendererService,
|
||||||
IScale,
|
IScale,
|
||||||
|
@ -129,6 +130,8 @@ export default class BaseLayer<ChildLayerStyleOptions = {}> extends EventEmitter
|
||||||
|
|
||||||
protected fontService: IFontService;
|
protected fontService: IFontService;
|
||||||
|
|
||||||
|
protected pickingService: IPickingService;
|
||||||
|
|
||||||
protected rendererService: IRendererService;
|
protected rendererService: IRendererService;
|
||||||
|
|
||||||
protected layerService: ILayerService;
|
protected layerService: ILayerService;
|
||||||
|
@ -258,6 +261,10 @@ export default class BaseLayer<ChildLayerStyleOptions = {}> extends EventEmitter
|
||||||
this.interactionService = this.container.get<IInteractionService>(
|
this.interactionService = this.container.get<IInteractionService>(
|
||||||
TYPES.IInteractionService,
|
TYPES.IInteractionService,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
this.pickingService = this.container.get<IPickingService>(
|
||||||
|
TYPES.IPickingService,
|
||||||
|
);
|
||||||
this.mapService = this.container.get<IMapService>(TYPES.IMapService);
|
this.mapService = this.container.get<IMapService>(TYPES.IMapService);
|
||||||
this.cameraService = this.container.get<ICameraService>(
|
this.cameraService = this.container.get<ICameraService>(
|
||||||
TYPES.ICameraService,
|
TYPES.ICameraService,
|
||||||
|
@ -815,6 +822,13 @@ export default class BaseLayer<ChildLayerStyleOptions = {}> extends EventEmitter
|
||||||
this.interactionService.triggerHover({ x, y });
|
this.interactionService.triggerHover({ x, y });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boxSelect(
|
||||||
|
box: [number, number, number, number],
|
||||||
|
cb: (...args: any[]) => void,
|
||||||
|
) {
|
||||||
|
this.pickingService.boxPickLayer(this, box, cb);
|
||||||
|
}
|
||||||
|
|
||||||
public buildLayerModel(
|
public buildLayerModel(
|
||||||
options: ILayerModelInitializationOptions &
|
options: ILayerModelInitializationOptions &
|
||||||
Partial<IModelInitializationOptions>,
|
Partial<IModelInitializationOptions>,
|
||||||
|
|
|
@ -8,6 +8,7 @@ import {
|
||||||
IRendererService,
|
IRendererService,
|
||||||
TYPES,
|
TYPES,
|
||||||
} from '@antv/l7-core';
|
} from '@antv/l7-core';
|
||||||
|
import { DOM } from '@antv/l7-utils';
|
||||||
import { inject, injectable } from 'inversify';
|
import { inject, injectable } from 'inversify';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -20,7 +20,7 @@ import {
|
||||||
Properties,
|
Properties,
|
||||||
} from '@turf/helpers';
|
} from '@turf/helpers';
|
||||||
import { EventEmitter } from 'eventemitter3';
|
import { EventEmitter } from 'eventemitter3';
|
||||||
import { cloneDeep, isFunction, isString } from 'lodash';
|
import { cloneDeep, isFunction, isString, merge } from 'lodash';
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
// tslint:disable-next-line:no-submodule-imports
|
// tslint:disable-next-line:no-submodule-imports
|
||||||
import Supercluster from 'supercluster/dist/supercluster';
|
import Supercluster from 'supercluster/dist/supercluster';
|
||||||
|
@ -37,6 +37,7 @@ export default class Source extends EventEmitter {
|
||||||
public hooks = {
|
public hooks = {
|
||||||
init: new SyncHook(),
|
init: new SyncHook(),
|
||||||
};
|
};
|
||||||
|
|
||||||
public parser: IParserCfg = { type: 'geojson' };
|
public parser: IParserCfg = { type: 'geojson' };
|
||||||
public transforms: ITransform[] = [];
|
public transforms: ITransform[] = [];
|
||||||
public cluster: boolean = false;
|
public cluster: boolean = false;
|
||||||
|
@ -51,6 +52,7 @@ export default class Source extends EventEmitter {
|
||||||
// 原始数据
|
// 原始数据
|
||||||
private originData: any;
|
private originData: any;
|
||||||
private rawData: any;
|
private rawData: any;
|
||||||
|
private cfg: any = {};
|
||||||
|
|
||||||
private clusterIndex: Supercluster;
|
private clusterIndex: Supercluster;
|
||||||
|
|
||||||
|
@ -58,6 +60,7 @@ export default class Source extends EventEmitter {
|
||||||
super();
|
super();
|
||||||
// this.rawData = cloneDeep(data);
|
// this.rawData = cloneDeep(data);
|
||||||
this.originData = data;
|
this.originData = data;
|
||||||
|
|
||||||
this.initCfg(cfg);
|
this.initCfg(cfg);
|
||||||
|
|
||||||
this.hooks.init.tap('parser', () => {
|
this.hooks.init.tap('parser', () => {
|
||||||
|
@ -75,7 +78,7 @@ export default class Source extends EventEmitter {
|
||||||
public setData(data: any, options?: ISourceCFG) {
|
public setData(data: any, options?: ISourceCFG) {
|
||||||
this.rawData = data;
|
this.rawData = data;
|
||||||
this.originData = data;
|
this.originData = data;
|
||||||
this.initCfg(options);
|
this.initCfg(this.cfg);
|
||||||
this.init();
|
this.init();
|
||||||
this.emit('update');
|
this.emit('update');
|
||||||
}
|
}
|
||||||
|
@ -160,7 +163,9 @@ export default class Source extends EventEmitter {
|
||||||
this.data = null;
|
this.data = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private initCfg(cfg?: ISourceCFG) {
|
private initCfg(option?: ISourceCFG) {
|
||||||
|
this.cfg = merge(this.cfg, option);
|
||||||
|
const cfg = this.cfg;
|
||||||
if (cfg) {
|
if (cfg) {
|
||||||
if (cfg.parser) {
|
if (cfg.parser) {
|
||||||
this.parser = cfg.parser;
|
this.parser = cfg.parser;
|
||||||
|
|
|
@ -155,8 +155,8 @@ export function printCanvas(canvas: HTMLCanvasElement) {
|
||||||
|
|
||||||
export function getViewPortScale() {
|
export function getViewPortScale() {
|
||||||
const meta = document.querySelector('meta[name="viewport"]');
|
const meta = document.querySelector('meta[name="viewport"]');
|
||||||
const contentItems = meta.content.split(',');
|
const contentItems = (meta as any).content?.split(',');
|
||||||
const scale = contentItems.find((item) => {
|
const scale = contentItems.find((item: string) => {
|
||||||
const [key, value] = item.split('=');
|
const [key, value] = item.split('=');
|
||||||
return key === 'initial-scale';
|
return key === 'initial-scale';
|
||||||
});
|
});
|
||||||
|
|
|
@ -14,7 +14,7 @@ export default class Point3D extends React.Component {
|
||||||
public async componentDidMount() {
|
public async componentDidMount() {
|
||||||
const scene = new Scene({
|
const scene = new Scene({
|
||||||
id: 'map',
|
id: 'map',
|
||||||
pickBufferScale: 3.0,
|
pickBufferScale: 1.0,
|
||||||
map: new GaodeMap({
|
map: new GaodeMap({
|
||||||
style: 'light',
|
style: 'light',
|
||||||
center: [-121.24357, 37.58264],
|
center: [-121.24357, 37.58264],
|
||||||
|
@ -59,23 +59,35 @@ export default class Point3D extends React.Component {
|
||||||
stroke: '#fff',
|
stroke: '#fff',
|
||||||
});
|
});
|
||||||
|
|
||||||
scene.addLayer(pointLayer);
|
const textLayer = new PointLayer({})
|
||||||
this.scene = scene;
|
.source(data, {
|
||||||
setTimeout(() => {
|
parser: {
|
||||||
console.log('updatedata');
|
type: 'csv',
|
||||||
pointLayer.setData(
|
x: 'Longitude',
|
||||||
{
|
y: 'Latitude',
|
||||||
type: 'FeatureCollection',
|
|
||||||
features: [],
|
|
||||||
},
|
},
|
||||||
{
|
})
|
||||||
parser: {
|
.shape('EventID', 'text')
|
||||||
type: 'geojson',
|
.size(8)
|
||||||
},
|
|
||||||
|
.color('red')
|
||||||
|
.style({
|
||||||
|
opacity: 1,
|
||||||
|
strokeWidth: 0,
|
||||||
|
stroke: '#fff',
|
||||||
|
});
|
||||||
|
|
||||||
|
scene.addLayer(pointLayer);
|
||||||
|
scene.addLayer(textLayer);
|
||||||
|
pointLayer.on('click', (e) => {
|
||||||
|
const res = pointLayer.boxSelect(
|
||||||
|
[e.x - 10, e.y - 10, e.x + 10, e.y + 10],
|
||||||
|
(fe) => {
|
||||||
|
console.log(fe);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
console.log(pointLayer);
|
});
|
||||||
}, 3000);
|
this.scene = scene;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue