From 5ef6bafc1437cea08ad9b292a392eb7ac5da3df3 Mon Sep 17 00:00:00 2001 From: YiQianYao <42212176+yiiiiiiqianyao@users.noreply.github.com> Date: Fri, 28 Oct 2022 15:44:22 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E7=93=A6=E7=89=87=E5=9B=BE=E5=B1=82?= =?UTF-8?q?=E4=BA=8B=E4=BB=B6=E8=8E=B7=E5=8F=96=E6=95=B0=E6=8D=AE=E3=80=81?= =?UTF-8?q?=E7=93=A6=E7=89=87=E6=95=B0=E6=8D=AE=E8=A7=A3=E6=9E=90=20(#1447?= =?UTF-8?q?)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: change vector tile get data * feat: 瓦片图层事件支持获取数据 * style: lint style Co-authored-by: shihui --- dev-demos/layer/control/exportImage.tsx | 1 + dev-demos/layer/control/hide.tsx | 5 +- dev-demos/layer/control/layerSwitch.tsx | 1 + dev-demos/layer/control/mapTheme.tsx | 4 +- dev-demos/layer/control/navigation.tsx | 4 +- dev-demos/layer/control/remove.tsx | 4 +- dev-demos/layer/control/setOptions.tsx | 1 + dev-demos/layer/popup/layerPopup.tsx | 4 +- dev-demos/tile/district/polygon.tsx | 9 +++- dev-demos/tile/district/worldmap.tsx | 33 ++++++------- .../src/control/baseControl/popperControl.ts | 3 ++ .../src/control/baseControl/selectControl.ts | 1 + packages/component/src/control/fullscreen.ts | 1 + packages/component/src/popup/layerPopup.ts | 1 + packages/component/src/popup/popup.ts | 2 + .../services/interaction/IPickingService.ts | 3 +- .../services/interaction/PickingService.ts | 3 +- .../src/tile/service/TileLayerService.ts | 1 + .../src/tile/service/TilePickService.ts | 28 +++++++++-- packages/layers/src/tile/tileFactory/Tile.ts | 21 ++++++++ .../layers/src/tile/tileFactory/VectorTile.ts | 7 +-- packages/source/src/parser/mvt.ts | 49 +------------------ packages/source/src/source.ts | 2 + 23 files changed, 98 insertions(+), 90 deletions(-) diff --git a/dev-demos/layer/control/exportImage.tsx b/dev-demos/layer/control/exportImage.tsx index f46f661d4c..cc7516fb22 100644 --- a/dev-demos/layer/control/exportImage.tsx +++ b/dev-demos/layer/control/exportImage.tsx @@ -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(); const [imgSrc, setImgSrc] = useState(''); const [control, setControl] = useState(null); diff --git a/dev-demos/layer/control/hide.tsx b/dev-demos/layer/control/hide.tsx index 236420a194..32bf473222 100644 --- a/dev-demos/layer/control/hide.tsx +++ b/dev-demos/layer/control/hide.tsx @@ -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', diff --git a/dev-demos/layer/control/layerSwitch.tsx b/dev-demos/layer/control/layerSwitch.tsx index 5a7494dfa1..bbbcb961c6 100644 --- a/dev-demos/layer/control/layerSwitch.tsx +++ b/dev-demos/layer/control/layerSwitch.tsx @@ -13,6 +13,7 @@ import { FunctionComponent, useEffect } from 'react'; const Demo: FunctionComponent = () => { const [layers, setLayers] = useState([]); + // eslint-disable-next-line @typescript-eslint/no-unused-vars const [scene, setScene] = useState(); const [newLayer, setNewLayer] = useState(null); const [control, setControl] = useState(null); diff --git a/dev-demos/layer/control/mapTheme.tsx b/dev-demos/layer/control/mapTheme.tsx index 7e44e1cc77..b269e9923a 100644 --- a/dev-demos/layer/control/mapTheme.tsx +++ b/dev-demos/layer/control/mapTheme.tsx @@ -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(); - useEffect(() => { const newScene = new Scene({ id: 'map', diff --git a/dev-demos/layer/control/navigation.tsx b/dev-demos/layer/control/navigation.tsx index 6cecbe23e1..ed10e01b20 100644 --- a/dev-demos/layer/control/navigation.tsx +++ b/dev-demos/layer/control/navigation.tsx @@ -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(); - useEffect(() => { const newScene = new Scene({ id: 'map', diff --git a/dev-demos/layer/control/remove.tsx b/dev-demos/layer/control/remove.tsx index b8dec3ac5f..5c724d1714 100644 --- a/dev-demos/layer/control/remove.tsx +++ b/dev-demos/layer/control/remove.tsx @@ -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(null); const [zoom, setZoom] = useState(null); diff --git a/dev-demos/layer/control/setOptions.tsx b/dev-demos/layer/control/setOptions.tsx index 4c6a21c821..732b338ea1 100644 --- a/dev-demos/layer/control/setOptions.tsx +++ b/dev-demos/layer/control/setOptions.tsx @@ -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', diff --git a/dev-demos/layer/popup/layerPopup.tsx b/dev-demos/layer/popup/layerPopup.tsx index 8aab616776..d4569e6b90 100644 --- a/dev-demos/layer/popup/layerPopup.tsx +++ b/dev-demos/layer/popup/layerPopup.tsx @@ -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(null); + // eslint-disable-next-line @typescript-eslint/no-unused-vars const [popup, setPopup] = useState(null); useEffect(() => { @@ -81,7 +83,7 @@ const Demo: FunctionComponent = () => { fields: [ { field: 'name', - formatField: (key) => { + formatField: () => { return '名称'; }, }, diff --git a/dev-demos/tile/district/polygon.tsx b/dev-demos/tile/district/polygon.tsx index f727c57a6a..021769db71 100644 --- a/dev-demos/tile/district/polygon.tsx +++ b/dev-demos/tile/district/polygon.tsx @@ -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); }); }, []); diff --git a/dev-demos/tile/district/worldmap.tsx b/dev-demos/tile/district/worldmap.tsx index 2fe4835875..5466c18995 100644 --- a/dev-demos/tile/district/worldmap.tsx +++ b/dev-demos/tile/district/worldmap.tsx @@ -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', diff --git a/packages/component/src/control/baseControl/popperControl.ts b/packages/component/src/control/baseControl/popperControl.ts index e492377c50..9d71175021 100644 --- a/packages/component/src/control/baseControl/popperControl.ts +++ b/packages/component/src/control/baseControl/popperControl.ts @@ -49,6 +49,7 @@ export default class PopperControl< */ public getDefault(option?: Partial): 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, diff --git a/packages/component/src/control/baseControl/selectControl.ts b/packages/component/src/control/baseControl/selectControl.ts index d6503fa9f8..8e8022e0c1 100644 --- a/packages/component/src/control/baseControl/selectControl.ts +++ b/packages/component/src/control/baseControl/selectControl.ts @@ -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, )!; diff --git a/packages/component/src/control/fullscreen.ts b/packages/component/src/control/fullscreen.ts index d76c5a8ec4..eb0f8eda54 100644 --- a/packages/component/src/control/fullscreen.ts +++ b/packages/component/src/control/fullscreen.ts @@ -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', diff --git a/packages/component/src/popup/layerPopup.ts b/packages/component/src/popup/layerPopup.ts index b4295a6ba5..b8dd4df448 100644 --- a/packages/component/src/popup/layerPopup.ts +++ b/packages/component/src/popup/layerPopup.ts @@ -170,6 +170,7 @@ export default class LayerPopup extends Popup { } } + // eslint-disable-next-line @typescript-eslint/no-unused-vars protected onLayerMouseOut(layer: ILayer, e: any) { this.displayFeatureInfo = undefined; if (this.isShow) { diff --git a/packages/component/src/popup/popup.ts b/packages/component/src/popup/popup.ts index 5202ce09e1..dc876792af 100644 --- a/packages/component/src/popup/popup.ts +++ b/packages/component/src/popup/popup.ts @@ -355,6 +355,7 @@ export default class Popup this.setPopupPosition(x, y); }; + // eslint-disable-next-line @typescript-eslint/no-unused-vars protected getDefault(option: Partial): O { // tslint:disable-next-line:no-object-literal-type-assertion return { @@ -401,6 +402,7 @@ export default class Popup } 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) { diff --git a/packages/core/src/services/interaction/IPickingService.ts b/packages/core/src/services/interaction/IPickingService.ts index 2a6e073d3c..0ab6491fc9 100644 --- a/packages/core/src/services/interaction/IPickingService.ts +++ b/packages/core/src/services/interaction/IPickingService.ts @@ -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 } \ No newline at end of file diff --git a/packages/core/src/services/interaction/PickingService.ts b/packages/core/src/services/interaction/PickingService.ts index 0a7339c4ea..a6e7872c21 100644 --- a/packages/core/src/services/interaction/PickingService.ts +++ b/packages/core/src/services/interaction/PickingService.ts @@ -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' diff --git a/packages/layers/src/tile/service/TileLayerService.ts b/packages/layers/src/tile/service/TileLayerService.ts index db384df356..f372a2b172 100644 --- a/packages/layers/src/tile/service/TileLayerService.ts +++ b/packages/layers/src/tile/service/TileLayerService.ts @@ -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( diff --git a/packages/layers/src/tile/service/TilePickService.ts b/packages/layers/src/tile/service/TilePickService.ts index 80679a67fa..3ac7be0b07 100644 --- a/packages/layers/src/tile/service/TilePickService.ts +++ b/packages/layers/src/tile/service/TilePickService.ts @@ -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; } } diff --git a/packages/layers/src/tile/tileFactory/Tile.ts b/packages/layers/src/tile/tileFactory/Tile.ts index b554beb534..ed6777477c 100644 --- a/packages/layers/src/tile/tileFactory/Tile.ts +++ b/packages/layers/src/tile/tileFactory/Tile.ts @@ -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[] = []; + 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()); } diff --git a/packages/layers/src/tile/tileFactory/VectorTile.ts b/packages/layers/src/tile/tileFactory/VectorTile.ts index eeb5fcb171..8bba133912 100644 --- a/packages/layers/src/tile/tileFactory/VectorTile.ts +++ b/packages/layers/src/tile/tileFactory/VectorTile.ts @@ -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', diff --git a/packages/source/src/parser/mvt.ts b/packages/source/src/parser/mvt.ts index 3f36f1dc8d..2f90075cfc 100644 --- a/packages/source/src/parser/mvt.ts +++ b/packages/source/src/parser/mvt.ts @@ -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 = { 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, - // coord: string, ): Promise => { 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[] = []; - 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, diff --git a/packages/source/src/source.ts b/packages/source/src/source.ts index 0400e12db6..89321ea41a 100644 --- a/packages/source/src/source.ts +++ b/packages/source/src/source.ts @@ -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)