feat: 升级图例方法、图例更新事件 (#1394)

* feat: 升级图例方法 & 图例更新事件

* feat: 升级图例方法 & 图例更新事件

* docs: legend 文档

Co-authored-by: 象数 <zhengxue.lzx@antgroup.com>
This commit is contained in:
@thinkinggis 2022-10-14 16:20:50 +08:00 committed by GitHub
parent 6754bf52d2
commit 6da24ed06a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 459 additions and 3 deletions

View File

@ -0,0 +1,135 @@
import { Scene, GaodeMapV2 } from "@antv/l7";
import React, { useEffect } from "react";
import { LineLayer, PointLayer } from "@antv/l7";
const lineList: Feature<LineString>[] = [
{
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<LineString>[]) => {
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<LineString> | 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 (
<div>
<div id={id} style={{ height: 400, position: "relative" }} />
</div>
);
};
export default Demo;

View File

@ -0,0 +1,3 @@
### source 更新
<code src="./demos/source-update.tsx"></code>

View File

@ -0,0 +1,3 @@
### 枚举类型
<code src="./cat.tsx"></code>

View File

@ -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<ILayer>();
const [mapScene, setScene] = useState<Scene>();
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 (
<div>
<div
id="map"
style={{
height: '500px',
position: 'relative',
}}
/>
<Button onClick={changeColor} type="primary" size={'large'}>
</Button>
</div>
);
};

View File

@ -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<IData | undefined>(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<IData2 | undefined>(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],
});
});
}

View File

@ -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;

View File

@ -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;
/**
*

View File

@ -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
})
}
}
}

View File

@ -19,6 +19,7 @@ import {
ILayerModelInitializationOptions,
ILayerPlugin,
ILayerService,
ILegend,
ILegendClassificaItem,
ILegendSegmentItem,
IMapService,
@ -1075,9 +1076,19 @@ export default class BaseLayer<ChildLayerStyleOptions = {}>
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 [];

View File

@ -52,6 +52,7 @@ export default class UpdateStyleAttributePlugin implements ILayerPlugin {
layer.getEncodedData(), // 获取经过 mapping 最新的数据
attribute.featureRange.startIndex,
attribute.featureRange.endIndex,
layer,
);
attribute.needRegenerateVertices = false;
});

View File

@ -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

View File

@ -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');
```