From 6da24ed06a402fc25599ef8ae4ede4c3c167d1c6 Mon Sep 17 00:00:00 2001 From: "@thinkinggis" Date: Fri, 14 Oct 2022 16:20:50 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=8D=87=E7=BA=A7=E5=9B=BE=E4=BE=8B?= =?UTF-8?q?=E6=96=B9=E6=B3=95=E3=80=81=E5=9B=BE=E4=BE=8B=E6=9B=B4=E6=96=B0?= =?UTF-8?q?=E4=BA=8B=E4=BB=B6=20(#1394)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: 升级图例方法 & 图例更新事件 * feat: 升级图例方法 & 图例更新事件 * docs: legend 文档 Co-authored-by: 象数 --- dev-demos/bugs/source/demos/source-update.tsx | 135 ++++++++++++++++ dev-demos/bugs/source/sourceUpdate.md | 3 + dev-demos/features/legend/cat.md | 3 + dev-demos/features/legend/cat.tsx | 85 +++++++++++ dev-demos/features/legend/useLine.ts | 144 ++++++++++++++++++ .../core/src/services/layer/ILayerService.ts | 14 +- .../services/layer/IStyleAttributeService.ts | 2 + .../services/layer/StyleAttributeService.ts | 8 +- packages/layers/src/core/BaseLayer.ts | 13 +- .../src/plugins/UpdateStyleAttributePlugin.ts | 1 + .../site/docs/common/layer/layer_event.md | 38 +++++ .../docs/common/layer/layer_interaction.md | 16 ++ 12 files changed, 459 insertions(+), 3 deletions(-) create mode 100644 dev-demos/bugs/source/demos/source-update.tsx create mode 100644 dev-demos/bugs/source/sourceUpdate.md create mode 100644 dev-demos/features/legend/cat.md create mode 100644 dev-demos/features/legend/cat.tsx create mode 100644 dev-demos/features/legend/useLine.ts diff --git a/dev-demos/bugs/source/demos/source-update.tsx b/dev-demos/bugs/source/demos/source-update.tsx new file mode 100644 index 0000000000..646d47e878 --- /dev/null +++ b/dev-demos/bugs/source/demos/source-update.tsx @@ -0,0 +1,135 @@ +import { Scene, GaodeMapV2 } from "@antv/l7"; +import React, { useEffect } from "react"; +import { LineLayer, PointLayer } from "@antv/l7"; +const lineList: Feature[] = [ + { + type: 'Feature', + properties: {}, + geometry: { + type: 'LineString', + coordinates: [ + [119.988511, 30.269614], + [119.9851, 30.269323], + [119.985438, 30.267852], + [119.990291, 30.267257], + [119.991454, 30.261762], + [119.994974, 30.256115], + [119.983641, 30.246146], + [119.985286, 30.241228], + [119.983351, 30.224089], + [119.985473, 30.221814], + [119.99271, 30.22088], + ], + }, + } + ]; +import { + coordAll, + Feature, + featureCollection, + LineString, + point +} from "@turf/turf"; +import { debounce } from "lodash"; + +const id = String(Math.random()); + +const getPointFeatureCollection = (lineList: Feature[]) => { + return featureCollection( + coordAll(featureCollection(lineList)).map((item) => point(item)) + ); +}; + +const Demo: React.FC = () => { + + useEffect(() => { + const scene = new Scene({ + id, + map: new GaodeMapV2({ + center: [120.151634, 30.244831], + pitch: 0, + style: "dark", + zoom: 10 + }) + }); + scene.on("loaded", () => { + const lineLayer = new LineLayer(); + lineLayer + .source( + featureCollection( + lineList.map((item, index) => { + item.properties = { + index + }; + return item; + }) + ) + ) + .size(4) + .color("#f00"); + const pointLayer = new PointLayer(); + pointLayer + .source(getPointFeatureCollection(lineList)) + .size(6) + .shape("circle") + .style({ + stroke: "#00f", + strokeWidth: 3 + }); + scene.addLayer(lineLayer); + scene.addLayer(pointLayer); + + let isDrag = false; + let dragFeature: Feature | null = null; + let prePosition = [0, 0]; + + lineLayer.on("mousedown", (e) => { + const { lng, lat } = e.lngLat; + prePosition = [lng, lat]; + + isDrag = true; + scene.setMapStatus({ + dragEnable: false + }); + dragFeature = e.feature; + }); + + scene.on("mousemove", (e) => { + requestAnimationFrame(() => { + if (isDrag) { + const { lng, lat } = e.lnglat; + const [lastLng, lastLat] = prePosition; + if (dragFeature) { + const positions = coordAll(dragFeature); + positions.forEach((position) => { + position[0] += lng - lastLng; + position[1] += lat - lastLat; + }); + dragFeature.geometry.coordinates = positions; + lineList[dragFeature.properties?.index] = dragFeature; + } + prePosition = [lng, lat]; + pointLayer.setData(getPointFeatureCollection([dragFeature])); + lineLayer.setData(featureCollection(lineList)); + // scene.render(); + } + }); + }); + + scene.on("mouseup", (e) => { + isDrag = false; + scene.setMapStatus({ + dragEnable: true + }); + }); + }); + }, []); + + return ( +
+
+
+ ); +}; + +export default Demo; diff --git a/dev-demos/bugs/source/sourceUpdate.md b/dev-demos/bugs/source/sourceUpdate.md new file mode 100644 index 0000000000..079f81d77c --- /dev/null +++ b/dev-demos/bugs/source/sourceUpdate.md @@ -0,0 +1,3 @@ +### source 更新 + + diff --git a/dev-demos/features/legend/cat.md b/dev-demos/features/legend/cat.md new file mode 100644 index 0000000000..296267a01d --- /dev/null +++ b/dev-demos/features/legend/cat.md @@ -0,0 +1,3 @@ +### 枚举类型 + + \ No newline at end of file diff --git a/dev-demos/features/legend/cat.tsx b/dev-demos/features/legend/cat.tsx new file mode 100644 index 0000000000..2615a856e9 --- /dev/null +++ b/dev-demos/features/legend/cat.tsx @@ -0,0 +1,85 @@ +import { ILayer, PolygonLayer, Scene } from '@antv/l7'; +import { Button } from 'antd'; + +import { Map } from '@antv/l7-maps'; +import React, { useEffect, useState } from 'react'; +import { useData, addLayers } from './useLine'; + +export default () => { + const { geoData } = useData(); + const [filllayer, setFillLayer] = useState(); + const [mapScene, setScene] = useState(); + const colors = [ + ['#7fc97f', '#beaed4', '#fdc086', '#ffff99', '#386cb0'], + ['#1b9e77', '#d95f02', '#7570b3', '#e7298a', '#66a61e'], + ['#a6cee3', '#1f78b4', '#b2df8a', '#33a02c', '#fb9a99'], + ['#fbb4ae', '#b3cde3', '#ccebc5', '#decbe4', '#fed9a6'], + ['#e41a1c', '#377eb8', '#4daf4a', '#984ea3', '#ff7f00'], + ['#8dd3c7', '#ffffb3', '#bebada', '#fb8072', '#80b1d3'], + ]; + + useEffect(() => { + const scene = new Scene({ + id: 'map', + map: new Map({ + pitch: 0, + style: 'light', + center: [-96, 37.8], + zoom: 3, + }), + }); + if (geoData) { + const layer = new PolygonLayer({}) + .source(geoData.county, { + transforms: [ + { + type: 'join', + sourceField: 'id', + targetField: 'id', + data: geoData.unemploymentdata, + }, + ], + }) + .shape('fill') + .color('name', colors[0]) + .style({ + opacity: 1, + }); + + scene.addLayer(layer); + setScene(scene); + + setFillLayer(layer); + layer.on('legend:color', (color) => { + console.log('color', color); + }); + addLayers(geoData, scene, layer); + } + return () => { + scene.destroy(); + }; + }, [geoData]); + + const changeColor = () => { + const index = Math.round(Math.random() * 6); + filllayer?.color('name', colors[index]); + mapScene?.render(); + console.log(filllayer?.getLegend('color')); + }; + + return ( +
+
+ + +
+ ); +}; diff --git a/dev-demos/features/legend/useLine.ts b/dev-demos/features/legend/useLine.ts new file mode 100644 index 0000000000..eeef94f6dc --- /dev/null +++ b/dev-demos/features/legend/useLine.ts @@ -0,0 +1,144 @@ +import { useEffect, useState } from 'react'; +import { LineLayer, PolygonLayer } from '@antv/l7'; +interface IData { + county: any; + state: any; + unemploymentdata: any; +} +interface IData2 { + country: any; + turnout: any; + } +export function useData() { + const [data, setData] = useState(undefined); + useEffect(() => { + Promise.all([ + fetch( + 'https://gw.alipayobjects.com/os/bmw-prod/5c4c7e02-a796-4c09-baba-629a99c909aa.json', + ).then((d) => d.json()), + // https://lab.isaaclin.cn/nCoV/api/area?latest=1 + fetch( + 'https://gw.alipayobjects.com/os/bmw-prod/738993d1-cc7e-4630-a318-80d6452fd125.csv', + ).then((d) => d.text()), + fetch( + ' https://gw.alipayobjects.com/os/bmw-prod/d13721bf-f0c2-4897-b6e6-633e6e022c09.json', + ).then((d) => d.json()), + ]).then(([county, unemployment, state]) => { + const unemploymentdata = unemployment + .split('\n') + .slice(0) + .map((line) => { + const item = line.split(','); + return { + id: item[0], + state: item[1], + county: item[2], + rate: item[3] * 1, + }; + }); + setData({ + county, + unemploymentdata, + state, + }); + }); + }, []); + + return { geoData: data }; +} + +export function useEuropeData() { + const [data, setData] = useState(undefined); + useEffect(() => { + Promise.all([ + fetch( + 'https://gw.alipayobjects.com/os/bmw-prod/01ff872b-99c6-4e41-bd3a-34c2134da597.json', + ).then((d) => d.json()), + // https://lab.isaaclin.cn/nCoV/api/area?latest=1 + fetch( + 'https://gw.alipayobjects.com/os/bmw-prod/64124f12-e086-4fe6-a900-bd57b410af69.csv', + ).then((d) => d.text()), + + ]).then(([country, turnoutData,]) => { + const turnout = turnoutData + .split('\n') + .slice(0) + .map((line) => { + const item = line.split(','); + return { + country: item[0], + turnout: item[1] *1, + }; + }); + setData({ + country, + turnout, + }); + }); + }, []); + + return { geoData: data }; + } + +export function addLayers(data: IData, scene, mainLayer) { + const linelayer = new PolygonLayer({}) + .source(data.county) + .size(0.5) + .shape('line') + .color('#fff') + .style({ + opacity: 1, + }); + const stateLayer = new PolygonLayer({}) + .source(data.state) + .size(1) + .shape('line') + .color('#fff') + .style({ + opacity: 1, + }); + + scene.addLayer(linelayer); + scene.addLayer(stateLayer); + addhightLayer(scene, mainLayer) + +} + +export function addEuropeLayers(data: IData2, scene, mainLayer) { + const linelayer = new PolygonLayer({}) + .source(data.country) + .size(0.5) + .shape('line') + .color('#fff') + .style({ + opacity: 1, + }); + + scene.addLayer(linelayer); + addhightLayer(scene, mainLayer) + } + + function addhightLayer(scene, mainLayer) { + const hightLayer = new LineLayer({ + zIndex: 4, // 设置显示层级 + name: 'hightlight', + }) + .source({ + type: 'FeatureCollection', + features: [], + }) + .shape('line') + .size(0.8) + .color('#000') + .style({ + opacity: 1, + }); + scene.addLayer(hightLayer); + mainLayer.on('click', (feature) => { + // console.log(feature) + hightLayer.setData({ + type: 'FeatureCollection', + features: [feature.feature], + }); + }); + } \ No newline at end of file diff --git a/packages/core/src/services/layer/ILayerService.ts b/packages/core/src/services/layer/ILayerService.ts index 1d4b966d9e..95fc3e31fb 100644 --- a/packages/core/src/services/layer/ILayerService.ts +++ b/packages/core/src/services/layer/ILayerService.ts @@ -31,6 +31,7 @@ import { IStyleAttribute, IStyleAttributeService, IStyleAttributeUpdateOptions, + ScaleTypeName, StyleAttrField, StyleAttributeField, StyleAttributeOption, @@ -106,8 +107,15 @@ export interface IActiveOption { type ILngLat = [number, number]; +export interface ILegend { + type: ScaleTypeName | undefined + field: StyleAttributeField | undefined; + items:LegendItems +} + // 分段图例 export interface ILegendSegmentItem { + field:string;// 图例字段 value: [number, number]; [key: string]: any; } @@ -241,6 +249,9 @@ export interface ITileLayerOPtions { export type LayerEventType = | 'inited' + | 'legend' + | 'legend:color' + | 'legend:size' | 'add' | 'remove' | 'destroy' @@ -384,7 +395,8 @@ export interface ILayer { style(options: unknown): ILayer; hide(): ILayer; show(): ILayer; - getLegendItems(name: string): LegendItems; + getLegendItems(name: string,index?: number): LegendItems; + getLegend(name: string):ILegend; setIndex(index: number): ILayer; isVisible(): boolean; setMaxZoom(min: number): ILayer; diff --git a/packages/core/src/services/layer/IStyleAttributeService.ts b/packages/core/src/services/layer/IStyleAttributeService.ts index 9d054ce1fc..74663318bb 100644 --- a/packages/core/src/services/layer/IStyleAttributeService.ts +++ b/packages/core/src/services/layer/IStyleAttributeService.ts @@ -4,6 +4,7 @@ import { } from '../renderer/IAttribute'; import { IBufferInitializationOptions } from '../renderer/IBuffer'; import { IElements } from '../renderer/IElements'; +import { ILayer } from './ILayerService'; /** * 1. 提供各个 Layer 样式属性初始值的注册服务 @@ -225,6 +226,7 @@ export interface IStyleAttributeService { features: IEncodeFeature[], startFeatureIdx?: number, endFeatureIdx?: number, + layer?: ILayer ): void; /** * 清除当前管理的所有属性 diff --git a/packages/core/src/services/layer/StyleAttributeService.ts b/packages/core/src/services/layer/StyleAttributeService.ts index acc3d6b063..508e88b790 100644 --- a/packages/core/src/services/layer/StyleAttributeService.ts +++ b/packages/core/src/services/layer/StyleAttributeService.ts @@ -6,7 +6,7 @@ import { gl } from '../renderer/gl'; import { IAttribute } from '../renderer/IAttribute'; import { IElements } from '../renderer/IElements'; import { IRendererService } from '../renderer/IRendererService'; -import { IWorkerOption } from './ILayerService'; +import { ILayer, IWorkerOption } from './ILayerService'; import { IAttributeScale, IEncodeFeature, @@ -121,6 +121,7 @@ export default class StyleAttributeService implements IStyleAttributeService { features: IEncodeFeature[], startFeatureIdx: number = 0, endFeatureIdx?: number, + layer?:ILayer ) { const attributeToUpdate = this.attributes.find( (attribute) => attribute.name === attributeName, @@ -179,6 +180,11 @@ export default class StyleAttributeService implements IStyleAttributeService { data: updatedBufferData, offset: bufferOffsetInBytes, }); + // size color 触发更新事件 + layer?.emit(`legend:${attributeName}`,{ + type:attributeName, + attr:attributeToUpdate + }) } } } diff --git a/packages/layers/src/core/BaseLayer.ts b/packages/layers/src/core/BaseLayer.ts index 8c69570535..9fc690e625 100644 --- a/packages/layers/src/core/BaseLayer.ts +++ b/packages/layers/src/core/BaseLayer.ts @@ -19,6 +19,7 @@ import { ILayerModelInitializationOptions, ILayerPlugin, ILayerService, + ILegend, ILegendClassificaItem, ILegendSegmentItem, IMapService, @@ -1075,9 +1076,19 @@ export default class BaseLayer return this.styleAttributeService.getLayerAttributeScale(name); } + public getLegend(name: string): ILegend { + const attribute = this.styleAttributeService.getLayerStyleAttribute(name); + const scales = attribute?.scale?.scalers || []; + + return { + type: scales[0].option?.type, + field: attribute?.scale?.field, + items: this.getLegendItems(name), + }; + } + public getLegendItems(name: string): LegendItems { const scale = this.styleAttributeService.getLayerAttributeScale(name); - // 函数自定义映射,没有 scale 返回为空数组 if (!scale) { return []; diff --git a/packages/layers/src/plugins/UpdateStyleAttributePlugin.ts b/packages/layers/src/plugins/UpdateStyleAttributePlugin.ts index 64c08ac208..e0ba5e006f 100644 --- a/packages/layers/src/plugins/UpdateStyleAttributePlugin.ts +++ b/packages/layers/src/plugins/UpdateStyleAttributePlugin.ts @@ -52,6 +52,7 @@ export default class UpdateStyleAttributePlugin implements ILayerPlugin { layer.getEncodedData(), // 获取经过 mapping 最新的数据 attribute.featureRange.startIndex, attribute.featureRange.endIndex, + layer, ); attribute.needRegenerateVertices = false; }); diff --git a/packages/site/docs/common/layer/layer_event.md b/packages/site/docs/common/layer/layer_event.md index 3e8984eae6..63dc212e95 100644 --- a/packages/site/docs/common/layer/layer_event.md +++ b/packages/site/docs/common/layer/layer_event.md @@ -39,6 +39,44 @@ layer.on('add', (type) => console.log(type)); layer.on('remove', (type) => console.log(type)); ``` +### legend +数据映射更新,图例发生变化,主要color、size + +参数 option + +- type 映射通道、图例类型 +- attr 映射实例 + + +```js +layer.on('legend', (ev) => console.log(ev)); + +``` + +### legend:color + +数据映射更新,图例发生变化,color 颜色改变 +参数 option +- type 映射通道、图例类型 +- attr 映射实例 + +```js +layer.on('legend:color', (ev) => console.log(ev)); + +``` + +### legend:size + +数据映射更新,图例发生变化,color 大小改变 +参数 option +- type 映射通道、图例类型 +- attr 映射实例 + +```js +layer.on('legend:color', (ev) => console.log(ev)); + +``` + ## 图层框选 ### boxSelect diff --git a/packages/site/docs/common/layer/layer_interaction.md b/packages/site/docs/common/layer/layer_interaction.md index aa7a81b1f1..1149b74c31 100644 --- a/packages/site/docs/common/layer/layer_interaction.md +++ b/packages/site/docs/common/layer/layer_interaction.md @@ -135,9 +135,25 @@ const size2 = sizeScale('n2'); // 20 获取图例配置 - type 图例类型 +- index 可选 默认 ```javascript layer.getLegendItems('color'); layer.getLegendItems('size'); +``` + +### getLegend(type: string) +获取图例 getLegendItems 加强版返回更多信息 + +返回值 +- type 图例类型 +- field 映射字段 +- items 图例项 + + +```javascript +layer.getLegend('color'); + +layer.getLegend('size'); ``` \ No newline at end of file