mirror of https://gitee.com/antv-l7/antv-l7
feat: 瓦片图层事件获取数据、瓦片数据解析 (#1447)
* feat: change vector tile get data * feat: 瓦片图层事件支持获取数据 * style: lint style Co-authored-by: shihui <yiqianyao.yqy@alibaba-inc.com>
This commit is contained in:
parent
ed3233cd0d
commit
5ef6bafc14
|
@ -4,6 +4,7 @@ import React, { useState } from 'react';
|
|||
import { FunctionComponent, useEffect } from 'react';
|
||||
|
||||
const Demo: FunctionComponent = () => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const [scene, setScene] = useState<Scene | undefined>();
|
||||
const [imgSrc, setImgSrc] = useState('');
|
||||
const [control, setControl] = useState<ExportImage | null>(null);
|
||||
|
|
|
@ -1,11 +1,10 @@
|
|||
import { GaodeMap, PositionType, Scene, MapTheme } from '@antv/l7';
|
||||
import { GaodeMap, Scene, MapTheme } from '@antv/l7';
|
||||
import React, { useState } from 'react';
|
||||
// tslint:disable-next-line:no-duplicate-imports
|
||||
import { FunctionComponent, useEffect } from 'react';
|
||||
|
||||
const POSITION_LIST = Object.values(PositionType);
|
||||
|
||||
const Demo: FunctionComponent = () => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const [zoom, setZoom] = useState(() => {
|
||||
return new MapTheme({
|
||||
position: 'topleft',
|
||||
|
|
|
@ -13,6 +13,7 @@ import { FunctionComponent, useEffect } from 'react';
|
|||
|
||||
const Demo: FunctionComponent = () => {
|
||||
const [layers, setLayers] = useState<ILayer[]>([]);
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const [scene, setScene] = useState<Scene | undefined>();
|
||||
const [newLayer, setNewLayer] = useState<ILayer | null>(null);
|
||||
const [control, setControl] = useState<LayerSwitch | null>(null);
|
||||
|
|
|
@ -1,11 +1,9 @@
|
|||
import { GaodeMap, Scene, MapTheme } from '@antv/l7';
|
||||
import React, { useState } from 'react';
|
||||
import React from 'react';
|
||||
// tslint:disable-next-line:no-duplicate-imports
|
||||
import { FunctionComponent, useEffect } from 'react';
|
||||
|
||||
const Demo: FunctionComponent = () => {
|
||||
const [scene, setScene] = useState<Scene | undefined>();
|
||||
|
||||
useEffect(() => {
|
||||
const newScene = new Scene({
|
||||
id: 'map',
|
||||
|
|
|
@ -1,12 +1,10 @@
|
|||
import { GaodeMapV2, GeoLocate, Scene } from '@antv/l7';
|
||||
import gcoord from 'gcoord';
|
||||
import React, { useState } from 'react';
|
||||
import React from 'react';
|
||||
// tslint:disable-next-line:no-duplicate-imports
|
||||
import { FunctionComponent, useEffect } from 'react';
|
||||
|
||||
const Demo: FunctionComponent = () => {
|
||||
const [scene, setScene] = useState<Scene | undefined>();
|
||||
|
||||
useEffect(() => {
|
||||
const newScene = new Scene({
|
||||
id: 'map',
|
||||
|
|
|
@ -1,10 +1,8 @@
|
|||
import { GaodeMap, PositionType, Scene, Zoom } from '@antv/l7';
|
||||
import { GaodeMap, Scene, Zoom } from '@antv/l7';
|
||||
import React, { useState } from 'react';
|
||||
// tslint:disable-next-line:no-duplicate-imports
|
||||
import { FunctionComponent, useEffect } from 'react';
|
||||
|
||||
const POSITION_LIST = Object.values(PositionType);
|
||||
|
||||
const Demo: FunctionComponent = () => {
|
||||
const [scene, setScene] = useState<Scene | null>(null);
|
||||
const [zoom, setZoom] = useState<Zoom | null>(null);
|
||||
|
|
|
@ -6,6 +6,7 @@ import { FunctionComponent, useEffect } from 'react';
|
|||
const POSITION_LIST = Object.values(PositionType);
|
||||
|
||||
const Demo: FunctionComponent = () => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const [zoom, setZoom] = useState(() => {
|
||||
return new Zoom({
|
||||
position: 'topleft',
|
||||
|
|
|
@ -12,7 +12,9 @@ import React, { useState } from 'react';
|
|||
import { FunctionComponent, useEffect } from 'react';
|
||||
|
||||
const Demo: FunctionComponent = () => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const [scene, setScene] = useState<Scene | null>(null);
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const [popup, setPopup] = useState<LayerPopup | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
|
@ -81,7 +83,7 @@ const Demo: FunctionComponent = () => {
|
|||
fields: [
|
||||
{
|
||||
field: 'name',
|
||||
formatField: (key) => {
|
||||
formatField: () => {
|
||||
return '名称';
|
||||
},
|
||||
},
|
||||
|
|
|
@ -33,9 +33,9 @@ export default () => {
|
|||
)
|
||||
// .shape('line')
|
||||
.color('COLOR')
|
||||
.active(true)
|
||||
// .active(true)
|
||||
.size(10)
|
||||
// .select(true)
|
||||
.select(true)
|
||||
|
||||
.style({
|
||||
// opacity: 0.3
|
||||
|
@ -84,6 +84,11 @@ export default () => {
|
|||
// );
|
||||
// });
|
||||
|
||||
layer.on('click', (e) => {
|
||||
console.log('click');
|
||||
console.log(e);
|
||||
});
|
||||
|
||||
scene.addLayer(point);
|
||||
});
|
||||
}, []);
|
||||
|
|
|
@ -3,7 +3,6 @@ import {
|
|||
Scene,
|
||||
Source,
|
||||
PolygonLayer,
|
||||
LineLayer,
|
||||
TileDebugLayer,
|
||||
PointLayer,
|
||||
} from '@antv/l7';
|
||||
|
@ -78,22 +77,22 @@ export default () => {
|
|||
}
|
||||
});
|
||||
|
||||
const line = new LineLayer({
|
||||
sourceLayer: 'WLD_L',
|
||||
zIndex: 2,
|
||||
})
|
||||
.source(source)
|
||||
.shape('line')
|
||||
.size(0.6)
|
||||
.color('type', (t) => {
|
||||
if (t === '0') {
|
||||
return 'red';
|
||||
}
|
||||
if (t === '2') {
|
||||
return '#09f';
|
||||
}
|
||||
return '#fc9272';
|
||||
});
|
||||
// const line = new LineLayer({
|
||||
// sourceLayer: 'WLD_L',
|
||||
// zIndex: 2,
|
||||
// })
|
||||
// .source(source)
|
||||
// .shape('line')
|
||||
// .size(0.6)
|
||||
// .color('type', (t) => {
|
||||
// if (t === '0') {
|
||||
// return 'red';
|
||||
// }
|
||||
// if (t === '2') {
|
||||
// return '#09f';
|
||||
// }
|
||||
// return '#fc9272';
|
||||
// });
|
||||
|
||||
const text = new PointLayer({
|
||||
sourceLayer: 'WLD',
|
||||
|
|
|
@ -49,6 +49,7 @@ export default class PopperControl<
|
|||
*/
|
||||
public getDefault(option?: Partial<O>): O {
|
||||
const defaultOption = super.getDefault(option);
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
const position = option?.position ?? defaultOption.position!;
|
||||
return {
|
||||
...super.getDefault(option),
|
||||
|
@ -73,8 +74,10 @@ export default class PopperControl<
|
|||
popperPlacement,
|
||||
popperTrigger,
|
||||
} = this.controlOption;
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
const popperContainer = this.mapsService.getMapContainer()!;
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
this.popper = new Popper(this.button!, {
|
||||
className: popperClassName,
|
||||
placement: popperPlacement,
|
||||
|
|
|
@ -70,6 +70,7 @@ export default class SelectControl<
|
|||
public setSelectValue(value: string | string[], emitEvent = true) {
|
||||
const finalValue = this.transSelectValue(value);
|
||||
this.optionDOMList.forEach((optionDOM) => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
const optionValue = optionDOM.getAttribute(
|
||||
SelectControlConstant.OptionValueAttrKey,
|
||||
)!;
|
||||
|
|
|
@ -47,6 +47,7 @@ export default class Fullscreen extends ButtonControl<
|
|||
public onAdd(): HTMLElement {
|
||||
const button = super.onAdd();
|
||||
button.addEventListener('click', this.onClick);
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
this.mapContainer = DOM.getContainer(this.scene.getSceneConfig().id!);
|
||||
this.mapContainer.addEventListener(
|
||||
'fullscreenchange',
|
||||
|
|
|
@ -170,6 +170,7 @@ export default class LayerPopup extends Popup<ILayerPopupOption> {
|
|||
}
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
protected onLayerMouseOut(layer: ILayer, e: any) {
|
||||
this.displayFeatureInfo = undefined;
|
||||
if (this.isShow) {
|
||||
|
|
|
@ -355,6 +355,7 @@ export default class Popup<O extends IPopupOption = IPopupOption>
|
|||
this.setPopupPosition(x, y);
|
||||
};
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
protected getDefault(option: Partial<O>): O {
|
||||
// tslint:disable-next-line:no-object-literal-type-assertion
|
||||
return {
|
||||
|
@ -401,6 +402,7 @@ export default class Popup<O extends IPopupOption = IPopupOption>
|
|||
}
|
||||
|
||||
protected updateFollowCursor(onlyClear?: boolean) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
const container = this.mapsService.getContainer()!;
|
||||
container.removeEventListener('mousemove', this.onMouseMove);
|
||||
if (this.popupOption.followCursor && !onlyClear) {
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { IInteractionTarget } from '../interaction/IInteractionService';
|
||||
import { ILayer } from '../layer/ILayerService';
|
||||
import { ILngLat } from '../map/IMapService';
|
||||
export interface IPickingService {
|
||||
pickedColors: Uint8Array | undefined;
|
||||
pickedTileLayers: ILayer[];
|
||||
|
@ -36,7 +37,7 @@ export interface ILayerPickService {
|
|||
* 获取选中的要素
|
||||
* @param id q
|
||||
*/
|
||||
getFeatureById(id: number):any
|
||||
getFeatureById(id: number, lngLat?: ILngLat):any
|
||||
|
||||
|
||||
}
|
|
@ -224,7 +224,8 @@ export default class PickingService implements IPickingService {
|
|||
pickedColors[2] !== 0
|
||||
) {
|
||||
const pickedFeatureIdx = decodePickingColor(pickedColors);
|
||||
const rawFeature = layer.layerPickService.getFeatureById(pickedFeatureIdx);
|
||||
|
||||
const rawFeature = layer.layerPickService.getFeatureById(pickedFeatureIdx, lngLat);
|
||||
if (
|
||||
pickedFeatureIdx !== layer.getCurrentPickId() &&
|
||||
type === 'mousemove'
|
||||
|
|
|
@ -38,6 +38,7 @@ export class TileLayerService {
|
|||
getTile(tileKey: string): ITile | undefined {
|
||||
return this._tiles.find((tile) => tile.key === tileKey);
|
||||
}
|
||||
|
||||
getVisibleTileBylngLat(lngLat: ILngLat): ITile | undefined {
|
||||
// 加载完成 & 可见 & 鼠标选中
|
||||
return this._tiles.find(
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import { ILayerService, ITile, ITilePickService } from '@antv/l7-core';
|
||||
import { ILayerService, ITile, ITilePickService, ILngLat, IInteractionTarget } from '@antv/l7-core';
|
||||
import { TileLayerService } from './TileLayerService';
|
||||
import { IInteractionTarget } from '@antv/l7-core';
|
||||
export interface ITilePickServiceOptions {
|
||||
layerService: ILayerService;
|
||||
tileLayerService: TileLayerService;
|
||||
|
@ -81,7 +80,30 @@ export class TilePickService implements ITilePickService{
|
|||
}
|
||||
|
||||
/** 从瓦片中根据数据 */
|
||||
getFeatureById() {
|
||||
getFeatureById(pickedFeatureIdx: number, lngLat: ILngLat) {
|
||||
const tile = this.tileLayerService.getVisibleTileBylngLat(lngLat)
|
||||
if (!tile) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const layers = tile.getLayers();
|
||||
let features = null;
|
||||
// 瓦片数据各自独立分布,没有完整的集合
|
||||
// TODO: 合并瓦片矢量数据,返回完整的的数据集
|
||||
layers.some(layer => {
|
||||
// 图层的 originData 可能并没有 id,因此我们使用编码后的 dataArray
|
||||
const data = layer.getSource().data.dataArray;
|
||||
|
||||
// _id 编码值可能根据字段进行编码,因此可能命中多个 feature
|
||||
const pickedFeature = data.filter(d => d._id === pickedFeatureIdx)
|
||||
|
||||
if(pickedFeature.length > 0) {
|
||||
features = pickedFeature;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
})
|
||||
return features;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { ILayer, createLayerContainer, ILngLat, ITile } from '@antv/l7-core';
|
||||
import { SourceTile } from '@antv/l7-utils';
|
||||
import { Container } from 'inversify';
|
||||
import { Feature, Properties } from '@turf/helpers';
|
||||
export default abstract class Tile implements ITile{
|
||||
public x: number;
|
||||
public y: number;
|
||||
|
@ -67,6 +68,26 @@ export default abstract class Tile implements ITile{
|
|||
});
|
||||
}
|
||||
|
||||
public getFeatures(sourceLayer: string | undefined){
|
||||
if(!sourceLayer || !this.sourceTile.data?.layers[sourceLayer]) return [];
|
||||
|
||||
const vectorTile = this.sourceTile.data?.layers[sourceLayer];
|
||||
const { x, y, z } = this.sourceTile;
|
||||
const features: Feature<GeoJSON.Geometry, Properties>[] = [];
|
||||
for( let i = 0; i < vectorTile.length; i++ ) {
|
||||
const vectorTileFeature = vectorTile.feature(i);
|
||||
const feature = vectorTileFeature.toGeoJSON(x, y, z);
|
||||
features.push({
|
||||
...feature,
|
||||
properties: {
|
||||
id: feature.id,
|
||||
...feature.properties,
|
||||
},
|
||||
})
|
||||
}
|
||||
return features;
|
||||
}
|
||||
|
||||
public destroy() {
|
||||
this.layers.forEach((layer) => layer.destroy());
|
||||
}
|
||||
|
|
|
@ -58,12 +58,7 @@ export default class VectorTile extends Tile {
|
|||
const { sourceLayer, featureId = 'id'} = this.parent.getLayerConfig<{
|
||||
featureId: string;
|
||||
}>();
|
||||
|
||||
const vectorLayer = this.sourceTile.data.layers[sourceLayer as string]
|
||||
if(!vectorLayer) {
|
||||
return false
|
||||
}
|
||||
const features = vectorLayer.features;
|
||||
const features = this.getFeatures(sourceLayer)
|
||||
return {
|
||||
data: {
|
||||
type: 'FeatureCollection',
|
||||
|
|
|
@ -6,12 +6,8 @@ import {
|
|||
TilesetManagerOptions,
|
||||
RequestParameters,
|
||||
} from '@antv/l7-utils';
|
||||
import {
|
||||
VectorTile,
|
||||
// VectorTileFeature,
|
||||
VectorTileLayer,
|
||||
} from '@mapbox/vector-tile';
|
||||
import { Feature, Properties } from '@turf/helpers';
|
||||
import { VectorTile, VectorTileLayer } from '@mapbox/vector-tile';
|
||||
import { Feature } from '@turf/helpers';
|
||||
import Protobuf from 'pbf';
|
||||
import { IParserData } from '../interface';
|
||||
import { ITileParserCFG } from '@antv/l7-core';
|
||||
|
@ -24,15 +20,6 @@ const DEFAULT_CONFIG: Partial<TilesetManagerOptions> = {
|
|||
warp: true,
|
||||
};
|
||||
|
||||
// const TILE_SIZE = 512;
|
||||
|
||||
export function osmTileXY2LonLat(x: number, y: number, zoom: number) {
|
||||
const lon = (x / Math.pow(2, zoom)) * 360 - 180;
|
||||
const n = Math.PI - (2 * Math.PI * y) / Math.pow(2, zoom);
|
||||
const lat = (180 / Math.PI) * Math.atan(0.5 * (Math.exp(n) - Math.exp(-n)));
|
||||
return [lon, lat];
|
||||
}
|
||||
|
||||
export type MapboxVectorTile = {
|
||||
layers: { [_: string]: VectorTileLayer & { features: Feature[] } };
|
||||
};
|
||||
|
@ -42,7 +29,6 @@ const getVectorTile = async (
|
|||
tileParams: TileLoadParams,
|
||||
tile: SourceTile,
|
||||
requestParameters?: Partial<RequestParameters>,
|
||||
// coord: string,
|
||||
): Promise<MapboxVectorTile> => {
|
||||
const tileUrl = getURLFromTemplate(url, tileParams);
|
||||
return new Promise((resolve) => {
|
||||
|
@ -53,40 +39,11 @@ const getVectorTile = async (
|
|||
},
|
||||
(err, data) => {
|
||||
if (err || !data) {
|
||||
// reject(err);
|
||||
resolve({ layers: {} });
|
||||
} else {
|
||||
const vectorTile = new VectorTile(
|
||||
new Protobuf(data),
|
||||
) as MapboxVectorTile;
|
||||
// check tile source layer
|
||||
for (const sourceLayer of Object.keys(vectorTile.layers)) {
|
||||
const features: Feature<GeoJSON.Geometry, Properties>[] = [];
|
||||
const vectorTileLayer = vectorTile.layers[sourceLayer];
|
||||
for (let i = 0; i < vectorTile.layers[sourceLayer].length; i++) {
|
||||
const vectorTileFeature = vectorTile.layers[sourceLayer].feature(
|
||||
i,
|
||||
);
|
||||
// let feature;
|
||||
// if (coord === 'lnglat') {
|
||||
const feature = vectorTileFeature.toGeoJSON(
|
||||
tileParams.x,
|
||||
tileParams.y,
|
||||
tileParams.z,
|
||||
);
|
||||
// TODO ID 统一编码
|
||||
features.push({
|
||||
...feature,
|
||||
properties: {
|
||||
id: feature.id,
|
||||
...feature.properties,
|
||||
},
|
||||
});
|
||||
}
|
||||
// @ts-ignore
|
||||
vectorTileLayer.features = features;
|
||||
}
|
||||
|
||||
resolve(vectorTile);
|
||||
}
|
||||
},
|
||||
|
@ -102,10 +59,8 @@ export default function mapboxVectorTile(
|
|||
// TODO: 后续考虑支持多服务
|
||||
const url = Array.isArray(data) ? data[0] : data;
|
||||
|
||||
// const coord = cfg?.coord || 'lnglat'; // lnglat - offset
|
||||
const getTileData = (tileParams: TileLoadParams, tile: SourceTile) =>
|
||||
getVectorTile(url, tileParams, tile, cfg?.requestParameters);
|
||||
// getVectorTile(data, tileParams, tile, coord);
|
||||
|
||||
const tilesetOptions = {
|
||||
...DEFAULT_CONFIG,
|
||||
|
|
|
@ -142,6 +142,7 @@ export default class Source extends EventEmitter implements ISource {
|
|||
}
|
||||
|
||||
public getFeatureById(id: number): unknown {
|
||||
|
||||
const { type = 'geojson', geometry } = this.parser as IParserCfg;
|
||||
if (type === 'geojson' && !this.cluster) {
|
||||
const feature =
|
||||
|
@ -150,6 +151,7 @@ export default class Source extends EventEmitter implements ISource {
|
|||
: 'null';
|
||||
const newFeature = cloneDeep(feature);
|
||||
|
||||
|
||||
if (
|
||||
newFeature?.properties &&
|
||||
(this.transforms.length !== 0 || this.dataArrayChanged)
|
||||
|
|
Loading…
Reference in New Issue