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/log/ILogService';
|
||||
export * from './services/interaction/IInteractionService';
|
||||
export * from './services/interaction/IPickingService';
|
||||
|
||||
/** 全部渲染服务接口 */
|
||||
export * from './services/renderer/IAttribute';
|
||||
|
|
|
@ -1,3 +1,10 @@
|
|||
import { ILayer } from '../layer/ILayerService';
|
||||
export interface IPickingService {
|
||||
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),
|
||||
);
|
||||
}
|
||||
|
||||
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) {
|
||||
if (this.alreadyInPicking || this.layerService.alreadyInRendering) {
|
||||
return;
|
||||
|
@ -81,13 +150,9 @@ export default class PickingService implements IPickingService {
|
|||
this.layerService.renderLayers();
|
||||
this.alreadyInPicking = false;
|
||||
}
|
||||
private async pickingLayers(target: IInteractionTarget) {
|
||||
const {
|
||||
getViewportSize,
|
||||
useFramebuffer,
|
||||
clear,
|
||||
getContainer,
|
||||
} = this.rendererService;
|
||||
|
||||
private resizePickingFBO() {
|
||||
const { getContainer } = this.rendererService;
|
||||
let {
|
||||
width,
|
||||
height,
|
||||
|
@ -102,6 +167,16 @@ export default class PickingService implements IPickingService {
|
|||
this.width = width;
|
||||
this.height = height;
|
||||
}
|
||||
}
|
||||
private async pickingLayers(target: IInteractionTarget) {
|
||||
const {
|
||||
getViewportSize,
|
||||
useFramebuffer,
|
||||
clear,
|
||||
getContainer,
|
||||
} = this.rendererService;
|
||||
|
||||
this.resizePickingFBO();
|
||||
|
||||
useFramebuffer(this.pickingFBO, () => {
|
||||
const layers = this.layerService.getLayers();
|
||||
|
@ -123,6 +198,7 @@ export default class PickingService implements IPickingService {
|
|||
});
|
||||
});
|
||||
}
|
||||
|
||||
private pickFromPickingFBO = (
|
||||
layer: ILayer,
|
||||
{ x, y, lngLat, type, target }: IInteractionTarget,
|
||||
|
|
|
@ -192,6 +192,10 @@ export interface ILayer {
|
|||
* 直接调用拾取方法,在非鼠标交互场景中使用
|
||||
*/
|
||||
pick(query: { x: number; y: number }): void;
|
||||
boxSelect(
|
||||
box: [number, number, number, number],
|
||||
cb: (...args: any[]) => void,
|
||||
): void;
|
||||
|
||||
updateLayerConfig(configToUpdate: Partial<ILayerConfig | unknown>): void;
|
||||
setAnimateStartTime(): void;
|
||||
|
|
|
@ -25,6 +25,7 @@ import {
|
|||
IModelInitializationOptions,
|
||||
IMultiPassRenderer,
|
||||
IPass,
|
||||
IPickingService,
|
||||
IPostProcessingPass,
|
||||
IRendererService,
|
||||
IScale,
|
||||
|
@ -129,6 +130,8 @@ export default class BaseLayer<ChildLayerStyleOptions = {}> extends EventEmitter
|
|||
|
||||
protected fontService: IFontService;
|
||||
|
||||
protected pickingService: IPickingService;
|
||||
|
||||
protected rendererService: IRendererService;
|
||||
|
||||
protected layerService: ILayerService;
|
||||
|
@ -258,6 +261,10 @@ export default class BaseLayer<ChildLayerStyleOptions = {}> extends EventEmitter
|
|||
this.interactionService = this.container.get<IInteractionService>(
|
||||
TYPES.IInteractionService,
|
||||
);
|
||||
|
||||
this.pickingService = this.container.get<IPickingService>(
|
||||
TYPES.IPickingService,
|
||||
);
|
||||
this.mapService = this.container.get<IMapService>(TYPES.IMapService);
|
||||
this.cameraService = this.container.get<ICameraService>(
|
||||
TYPES.ICameraService,
|
||||
|
@ -815,6 +822,13 @@ export default class BaseLayer<ChildLayerStyleOptions = {}> extends EventEmitter
|
|||
this.interactionService.triggerHover({ x, y });
|
||||
}
|
||||
|
||||
public boxSelect(
|
||||
box: [number, number, number, number],
|
||||
cb: (...args: any[]) => void,
|
||||
) {
|
||||
this.pickingService.boxPickLayer(this, box, cb);
|
||||
}
|
||||
|
||||
public buildLayerModel(
|
||||
options: ILayerModelInitializationOptions &
|
||||
Partial<IModelInitializationOptions>,
|
||||
|
|
|
@ -8,6 +8,7 @@ import {
|
|||
IRendererService,
|
||||
TYPES,
|
||||
} from '@antv/l7-core';
|
||||
import { DOM } from '@antv/l7-utils';
|
||||
import { inject, injectable } from 'inversify';
|
||||
|
||||
/**
|
||||
|
|
|
@ -20,7 +20,7 @@ import {
|
|||
Properties,
|
||||
} from '@turf/helpers';
|
||||
import { EventEmitter } from 'eventemitter3';
|
||||
import { cloneDeep, isFunction, isString } from 'lodash';
|
||||
import { cloneDeep, isFunction, isString, merge } from 'lodash';
|
||||
// @ts-ignore
|
||||
// tslint:disable-next-line:no-submodule-imports
|
||||
import Supercluster from 'supercluster/dist/supercluster';
|
||||
|
@ -37,6 +37,7 @@ export default class Source extends EventEmitter {
|
|||
public hooks = {
|
||||
init: new SyncHook(),
|
||||
};
|
||||
|
||||
public parser: IParserCfg = { type: 'geojson' };
|
||||
public transforms: ITransform[] = [];
|
||||
public cluster: boolean = false;
|
||||
|
@ -51,6 +52,7 @@ export default class Source extends EventEmitter {
|
|||
// 原始数据
|
||||
private originData: any;
|
||||
private rawData: any;
|
||||
private cfg: any = {};
|
||||
|
||||
private clusterIndex: Supercluster;
|
||||
|
||||
|
@ -58,6 +60,7 @@ export default class Source extends EventEmitter {
|
|||
super();
|
||||
// this.rawData = cloneDeep(data);
|
||||
this.originData = data;
|
||||
|
||||
this.initCfg(cfg);
|
||||
|
||||
this.hooks.init.tap('parser', () => {
|
||||
|
@ -75,7 +78,7 @@ export default class Source extends EventEmitter {
|
|||
public setData(data: any, options?: ISourceCFG) {
|
||||
this.rawData = data;
|
||||
this.originData = data;
|
||||
this.initCfg(options);
|
||||
this.initCfg(this.cfg);
|
||||
this.init();
|
||||
this.emit('update');
|
||||
}
|
||||
|
@ -160,7 +163,9 @@ export default class Source extends EventEmitter {
|
|||
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.parser) {
|
||||
this.parser = cfg.parser;
|
||||
|
|
|
@ -155,8 +155,8 @@ export function printCanvas(canvas: HTMLCanvasElement) {
|
|||
|
||||
export function getViewPortScale() {
|
||||
const meta = document.querySelector('meta[name="viewport"]');
|
||||
const contentItems = meta.content.split(',');
|
||||
const scale = contentItems.find((item) => {
|
||||
const contentItems = (meta as any).content?.split(',');
|
||||
const scale = contentItems.find((item: string) => {
|
||||
const [key, value] = item.split('=');
|
||||
return key === 'initial-scale';
|
||||
});
|
||||
|
|
|
@ -14,7 +14,7 @@ export default class Point3D extends React.Component {
|
|||
public async componentDidMount() {
|
||||
const scene = new Scene({
|
||||
id: 'map',
|
||||
pickBufferScale: 3.0,
|
||||
pickBufferScale: 1.0,
|
||||
map: new GaodeMap({
|
||||
style: 'light',
|
||||
center: [-121.24357, 37.58264],
|
||||
|
@ -59,23 +59,35 @@ export default class Point3D extends React.Component {
|
|||
stroke: '#fff',
|
||||
});
|
||||
|
||||
scene.addLayer(pointLayer);
|
||||
this.scene = scene;
|
||||
setTimeout(() => {
|
||||
console.log('updatedata');
|
||||
pointLayer.setData(
|
||||
{
|
||||
type: 'FeatureCollection',
|
||||
features: [],
|
||||
const textLayer = new PointLayer({})
|
||||
.source(data, {
|
||||
parser: {
|
||||
type: 'csv',
|
||||
x: 'Longitude',
|
||||
y: 'Latitude',
|
||||
},
|
||||
{
|
||||
parser: {
|
||||
type: 'geojson',
|
||||
},
|
||||
})
|
||||
.shape('EventID', 'text')
|
||||
.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