mirror of https://gitee.com/antv-l7/antv-l7
feat: 升级图例方法、图例更新事件 (#1394)
* feat: 升级图例方法 & 图例更新事件 * feat: 升级图例方法 & 图例更新事件 * docs: legend 文档 Co-authored-by: 象数 <zhengxue.lzx@antgroup.com>
This commit is contained in:
parent
6754bf52d2
commit
6da24ed06a
|
@ -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;
|
|
@ -0,0 +1,3 @@
|
|||
### source 更新
|
||||
|
||||
<code src="./demos/source-update.tsx"></code>
|
|
@ -0,0 +1,3 @@
|
|||
### 枚举类型
|
||||
|
||||
<code src="./cat.tsx"></code>
|
|
@ -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>
|
||||
);
|
||||
};
|
|
@ -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],
|
||||
});
|
||||
});
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
/**
|
||||
* 清除当前管理的所有属性
|
||||
|
|
|
@ -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
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 [];
|
||||
|
|
|
@ -52,6 +52,7 @@ export default class UpdateStyleAttributePlugin implements ILayerPlugin {
|
|||
layer.getEncodedData(), // 获取经过 mapping 最新的数据
|
||||
attribute.featureRange.startIndex,
|
||||
attribute.featureRange.endIndex,
|
||||
layer,
|
||||
);
|
||||
attribute.needRegenerateVertices = false;
|
||||
});
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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');
|
||||
```
|
Loading…
Reference in New Issue