feat: scale update 不生效 (#1574)

* feat: scale update

* docs: scale/filter document

* docs: this image is missing a text alternative
This commit is contained in:
@thinkinggis 2023-01-16 16:37:40 +08:00 committed by GitHub
parent 0587b41e1b
commit cefe7a71ed
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 431 additions and 71 deletions

View File

@ -2530,6 +2530,9 @@ export default () => {
// 'rgb(8,81,156)'
// ]
// )
.scale('num',{
type:'quantile'
})
.color("num", ["#E8F1FF", "#A8BDEC", "#688DE4", "#3461CA", "#23396E"])
.shape("fill")
.select({
@ -2567,6 +2570,14 @@ export default () => {
scene.addLayer(chinaPolygonLayer);
scene.addLayer(layer2);
setTimeout(()=>{
chinaPolygonLayer.scale('num',{
type:'quantize'
})
// chinaPolygonLayer.color('num',['#ffffb2','#fed976','#feb24c','#fd8d3c','#f03b20','#bd0026'])
scene.render()
console.log('render')
},3000)
});
fetch(
"https://gw.alipayobjects.com/os/bmw-prod/c4a6aa9d-8923-4193-a695-455fd8f6638c.json" // 标注数据

View File

@ -243,7 +243,6 @@ const data = {
}
]
}
console.log(data);
export default () => {
useEffect(() => {
const scene = new Scene({

View File

@ -0,0 +1,41 @@
import { Scene } from '@antv/l7';
import { DrawPolygon } from '@antv/l7-draw';
import { Map } from '@antv/l7-maps';
import React, { useEffect, useState } from 'react';
const id = String(Math.random());
const Demo: React.FC = () => {
const [, setLineDrawer] = useState<DrawPolygon | null>(null);
useEffect(() => {
const scene = new Scene({
id,
map: new Map({
center: [120.151634, 30.244831],
pitch: 0,
style: 'dark',
zoom: 10,
}),
});
scene.on('mousedown', () => {
console.log('mousedown');
});
scene.on('loaded', () => {
const drawer = new DrawPolygon(scene, {
// liveUpdate: true,
});
setLineDrawer(drawer);
drawer.enable();
});
}, []);
return (
<div>
<div id={id} style={{ height: 400, position: 'relative' }} />
</div>
);
};
export default Demo;

View File

@ -3,7 +3,7 @@ title: 展示距离和面积
order: 3
group:
path: /polygon
title: 绘制
title: 图形绘制
order: 3
---

View File

@ -0,0 +1,9 @@
---
title: 绘制面
order: 3
group:
path: /polygon
order: 3
---
<code src="./demo/drawpolygon.tsx" compact="true" defaultShowCode="true"></code>

View File

@ -15,7 +15,7 @@ Range 和 domain 是 Scale 中非常重要的两个参数
| 分类 | cat、timeCat |
| 常量 | identity |
在使用 `L7` 开发过程中默认情况下不需要进行度量的配置,因为 `G2` 代码内部已经根据数据的形式对度量进行了假设,其计算过程如下:
在使用 `L7` 开发过程中默认情况下不需要进行度量的配置,因为 `L7` 代码内部已经根据数据的形式对度量进行了假设,其计算过程如下:
查看用户是否制定了对应字段的数据类型 `type`)
如果没有,判断字段的第一条数据的字段类型

View File

@ -194,6 +194,7 @@ export interface IStyleAttributeService {
registerStyleAttribute(
options: Partial<IStyleAttributeInitializationOptions>,
): IStyleAttribute;
updateScaleAttribute(scale: IScaleOptions):void;
updateStyleAttribute(
attributeName: string,
attributeOptions: Partial<IStyleAttributeInitializationOptions>,

View File

@ -9,6 +9,7 @@ import { IRendererService } from '../renderer/IRendererService';
import { ILayer, IWorkerOption } from './ILayerService';
import {
IAttributeScale,
IScaleOptions,
IEncodeFeature,
IStyleAttribute,
IStyleAttributeInitializationOptions,
@ -70,13 +71,24 @@ export default class StyleAttributeService implements IStyleAttributeService {
return attributeToUpdate;
}
public updateScaleAttribute(scaleOption: IScaleOptions) {
this.attributes.forEach((attr:IStyleAttribute)=>{
const name = attr.name;
const field = attr.scale?.field as string;
if(scaleOption[name] || (field && scaleOption[field])) { // 字段类型和映射类型
attr.needRescale = true;
attr.needRemapping = true;
attr.needRegenerateVertices = true;
}
})
}
public updateStyleAttribute(
attributeName: string,
options: Partial<IStyleAttributeInitializationOptions>,
updateOptions?: Partial<IStyleAttributeUpdateOptions>,
) {
let attributeToUpdate = this.getLayerStyleAttribute(attributeName);
if (!attributeToUpdate) {
attributeToUpdate = this.registerStyleAttribute({

View File

@ -672,6 +672,11 @@ export default class BaseLayer<ChildLayerStyleOptions = {}>
} else {
this.scaleOptions[field] = cfg;
}
if (this.styleAttributeService) {
const scaleOptions = isObject(field) ? field : { [field]: cfg };
this.styleAttributeService.updateScaleAttribute(scaleOptions);
}
return this;
}
@ -1103,7 +1108,6 @@ export default class BaseLayer<ChildLayerStyleOptions = {}>
public getScaleOptions() {
return this.scaleOptions;
}
public encodeDataLength: number = 0;
public setEncodedData(encodedData: IEncodeFeature[]) {
this.encodedData = encodedData;

View File

@ -14,6 +14,7 @@ L7 Layer 接口设计遵循图形语法所有图层都继承于基类baseL
```javascript
const layer = new BaseLayer(option) // option - 传入构造函数的参数对象,提供 layer 的初始状态
.source(...) // 传入图层需要的数据以及相关的解析器
.filter() // 数据过滤方法
.shape(...) // 为图层指定具体的形状circle/triangle 等
.color(...) // 指定图层的颜色配置
.texture(...) // 指定图层引用的纹理

View File

@ -1,5 +1,5 @@
### source
### source 数据
设置图层数据以及解析配置 `source(data, config)`
@ -38,14 +38,63 @@ layer.source(data, {
],
});
```
### cluster
### scale(field: string, scaleConfig: IScaleConfig)
我们在使用 `cluster` 配置聚合图之后就可以使用一些聚合方法来获取对应参数。
#### getClusters(zoom: number): IFeatureCollection
获取指定缩放等级的聚合数据
- `zoom` 缩放等级
#### getClustersLeaves(id: string): IFeatureCollection
根据 `id` 获取聚合节点的数据,每个聚合节点会有一个唯一 `ID`
- `id` 聚合节点的 `id`
```ts
const source = layer.getSource();
source.getClustersLeaves(id);
layer.on('click', (e) => {
console.log(source.getClustersLeaves(e.feature.cluster_id));
});
```
## scale 数据度量
Scale 度量是将地图数据值(数字、日期、类别等数据)转成视觉值(颜色、大小、形状)。尺度 Scale 是数据可视化的基本组成部分,因为它们决定了视觉编码的性质。 L7 目前支持连续、离散、枚举类型数据的Scale并支持位置、形状、大小和颜色编码的映射。
在使用 L7 过程中,默认情况下不需要进行 Scale 的配置,因为 L7 会根据数据类型对 scale 推断,推断过程如下:
查看用户是否设置了 Scale如果没有:
判断字段的第一条数据的字段类型,如果数据中不存在对应的字段:
认为是常量为固定值
如果是数字则为 'linear';
如果是字符串类型 'cat';
### scale
![Scale 详细介绍](https://mp.weixin.qq.com/s/QyD1_ypu0PDwMxEz45v6Jg)
参数: (field: string, scaleOptions: IscaleOptions)
- `field` 指定 source 中传入的数据中用于映射的字段名
- `scaleConfig` 列定义配置,对象类型
- `scaleOptions` 列定义配置,对象类型
- type scale 类型
- unknown 未匹配颜色 可选 默认透明
- domain 值域 可选
```javascript
interface IScaleConfig {
interface IscaleOptions {
type: ScaleTypeName;
domain?: any[];
...
@ -59,19 +108,23 @@ layer.color('id', ['#f00', '#ff0'])
})
```
### ScaleTypeName
#### 类型
`scale` 的类型可以分为 `3``11` 种,不同 `Scale` 的差异在于 `domain->range` 的转换方法的不同。
`range``domain``Scale` 中非常重要的两个参数。
Range 和 domain 是 Scale 中非常重要的两个参数
domain: 地图数据值的定义区间
range视觉值的区间
不同Scale 的差异在于 domain->range 的转换方法的不同
- domain: 地图数据值的定义区间
- range视觉值的区间定义
| 数据类| 度量类型 |
| -------- | ------- |
| 连续 | linear、log、pow、time、sequential、quantize、quantile、threshold |
| 分类 | cat、time |
| 常量 | identity |
|数据类|度量类型|
|-----|------|
| 连续 | linear、log、pow |
| 连续分类 | quantize quantile,threshold,diverging |
| 分类 枚举 | cat |
#### Cat
@ -102,7 +155,18 @@ layer.color('t', ['red', 'white', 'blue']);
#### identify
常量度量 某个字段是不变的常量。
数据值和映射值相同
比如数据中value 字段记录了每个要素的颜色数值既为要映射的结果值s
```
// 设置为 identify
layer.scale('value', { type: 'identify' });
// 或者
layer.scale('value'); // L7 能够自动推断为 identify
```
#### linear
@ -131,58 +195,33 @@ layer.color('t', ['red', 'white', 'blue']);
1000 => "blue
```
#### diverging || Sequential
用于返回给定的颜色数组的统一非有理 B-spline 插值器函数该数组将转换为RGB颜色。
#### diverging
```js
const scaleColors = d3interpolate.interpolateRgbBasis(colors);
```
离散分类通常与两种相反的色调一起使用,以显示从负值到中心到正值的变化。这些类型的地图显示了彼此相关的值的大小。
#### IScaleConfig
```js
interface IScaleConfig {
type: ScaleTypeName;
domain?: any[];
range?: any[];
neutral?: number;
field?: string;
unknown?: string;
ticks?: any[];
nice?: boolean;
clamp?: boolean;
format?: () => any;
}
```
### cluster
我们在使用 `cluster` 配置聚合图之后就可以使用一些聚合方法来获取对应参数。
#### getClusters(zoom: number): IFeatureCollection
获取指定缩放等级的聚合数据
- `zoom` 缩放等级
#### getClustersLeaves(id: string): IFeatureCollection
根据 `id` 获取聚合节点的数据,每个聚合节点会有一个唯一 `ID`
- `id` 聚合节点的 `id`
```javascript
const source = layer.getSource();
source.getClustersLeaves(id);
layer.on('click', (e) => {
console.log(source.getClustersLeaves(e.feature.cluster_id));
});
```
## 视觉编码方法
可视化编码是将数据转换为可视形式的过程L7 目前支持形状,大小,颜色 3 种视觉通道,你可以指定数据字段,为不同要素设置不同的图形属性。
<img width="100%" style="display: block;margin: 0 auto;" alt="案例" src='https://gw.alipayobjects.com/mdn/rms_816329/afts/img/A*PzoTRJnY-fIAAAAAAAAAAAAAARQnAQ'>
### filter
数据过滤方法,支持回调函数将数据映射为true | false, 结果为true 时可见
```ts
pointLayer.size('type', (type) => {
// 回调函数
if (type === 'a') {
return false;
}
return true ;
});
```
### size
将数据值映射到图形的大小上的方法size 方法具体的参数使用可以查看对应图层的详细文档
@ -250,12 +289,16 @@ layer.color('type*value', (type, value) => {
#### color(value)
参数:`value` string
只支持接收一个参数value 可以是:
- 映射至颜色属性的数据源字段名,如果数据源中不存在这个字段名的话,则按照常量进行解析,这个时候会使用 L7 默认提供的颜色。
- 也可以直接指定某一个具体的颜色值 color如 '#fff', 'white','rgba(255,0,0,0.5)' ,rgb(255,0,1) 等。
如果数据为映射到颜色默认为透明色不显示如果需要设置该颜色需要在scale 中设置
示例
```javascript
@ -274,7 +317,11 @@ layer.color('white'); // 指定颜色
colors 的参数有以下情况:  如果为空,即未指定颜色的数组,那么使用内置的全局的颜色;如果需要指定颜色,则需要以数组格式传入,那么分类的颜色按照数组中的颜色确定。
```javascript
layer.color('name'); // 使用默认的颜色
layer.scale('name',{
type:'quantile'
unknown:'#ccc' // 设置无效颜色
})
layer.color('name'); // 使用identity
layer.color('name', ['red', 'blue']); // 使用传入的指定颜色
```

View File

@ -1,4 +1,4 @@
### 纹理方法
### texture
目前只在线图层上支持了纹理方法

View File

@ -2,21 +2,39 @@
如果已经添加了图层,需要修改图层显示样式可以再次调用图形映射方法,然后调用 `scene.render()`更新渲染即可
### 样式更新
### scale 更新
重新调用scale 方法
```tsx
layer.scale('value',{
type:'quantile'
})
scene.render();
```
### 数据映射
重新调用 color/size/filter/shape等方法
```javascript
layer.color('blue');
layer.size(10);
layer.style({});
scene.render();
```
### shape 更新
在在某些场景下切换 shape 的时候,我们需要重新构建图层元素的顶点构造。这意味着我们简单的改变当前图层的单一属性就达到更新图层的目的。
L7 已经为某些图层的 shape 切换做了额外的处理,如 PointLayer 的 "circle" 切换 "cylinder" 等,具体哪些图层盒支持直接切换则需要用户查阅具体图层的文档。
### layer.style
🌟 在不支持直接切换 shape 的时候建议重新创建图层
```javascript
layer.style({
opacity:1
});
scene.render();
```
### setData(data, option?: {})

View File

@ -0,0 +1,5 @@
---
title: Scale
order: 1
---
<embed src="@/docs/tutorial/scale.zh.md"></embed>

View File

@ -0,0 +1,212 @@
---
title: Scale 度量
order: 2
---
<embed src="@/docs/common/style.md"></embed>
## Scale 简介
Scale 度量是用于将地图数据值数字、日期、类别等数据转成视觉变量颜色、大小、形状。Scale 是数据可视化的基本元素因为它们决定了数据视觉编码的方式。L7 目前支持连续、离散、枚举类型等常用的 Scale。
![L7 Scale](https://mdn.alipayobjects.com/huamei_qa8qxu/afts/img/A*AOIvTpmPOmgAAAAAAAAAAAAADmJ7AQ/original)
## Scale 中的两个重要概念:
Range 和 Domain
Domain: 地图数据值的定义区间
Range视觉值的区间定义
不同 Scale 的差异在于 Domain-> Range 的转换方法不同
|数据类|度量类型|
|----|------|
| 连续 | linear、log、pow |
| 连续分类 | quantize quantile,threshold,diverging |
| 分类 枚举| cat |
## L7 Scale
在使用 L7 过程中,默认情况下不需要进行 Scale 的配置,因为 L7 会根据数据类型对 scale 推断,推断过程如下:
查看用户是否设置了 Scale如果没有:
判断字段的第一条数据的字段类型,如果数据中不存在对应的字段:
认为是常量为固定值
如果是数字则为 'linear';
如果是字符串类型 'cat';
## Cat 枚举
Cat 指枚举类型,用于展示分类数据,比如农作物种植区分布图,水稻、玉米、大豆等不同类别需要映射为不同的颜色。在 L7 如果判断字段的值为字符串,将认为是 Cat 类型并自动获取类型的唯一值,设置为 domain 。这样三种作物就会被一一映射成对应的颜色。
```tsx
const data = [
{
type:'A',
x: 110,
y:30
},
{
type:'B',
x: 110,
y:32
},
{
type:'C',
x: 110,
y:31
}
,{
type:'D',
x: 111,
y:33
}
,{
type:'E',
x: 112,
y:30
}
,{
type:'F',
x: 110,
y:30
}
]
layer.color('type',['red','white','blue','yellow'])
```
如上面的代码所示,图层没有设置 Scale, L7 根据第一个数据"A"的类型 ,推断为枚举类型。同时取出该字段的去重后的所有值 ['A','B','C','D','E','F'] 设置为 scale 的 domain, 那么 range 就是对应的颜色 ['red','white','blue','yellow']。
![L7 scale Cat映射](https://mdn.alipayobjects.com/huamei_qa8qxu/afts/img/A*rO53SYNk8hgAAAAAAAAAAAAADmJ7AQ/original)
如果不设置 domainL7 内部会自动计算 domaindomain 顺序跟数据顺序相关。如果希望固定domain 可自己设置 domain。domain 数值 和 range数值 一一对应。
```
layer.scale('type', {
type: 'cat',
domain: ['B', 'A', 'C', 'D'],
});
layer.color('type', ['red', 'white', 'blue', 'yellow']);
```
layer.scale('type', {
type: 'cat',
domain: ['B', 'A', 'C', 'D'],
});
layer.color('type', ['red', 'white', 'blue', 'yellow']);
```
![L7 Cat ](https://mdn.alipayobjects.com/huamei_qa8qxu/afts/img/A*kyP2RpUXdGUAAAAAAAAAAAAADmJ7AQ/original)
## Linear 连续线性
线性是连续数据的映射方法数据和视觉值是通过线性方法计算的。如数据范围domain [0,1000] 线性映射到 range [red,blue] 渐变色,就是依据线性函完成一一转换。
![L7 Scale 线性]('https://mdn.alipayobjects.com/huamei_qa8qxu/afts/img/A*Z_rGRr-jgI0AAAAAAAAAAAAADmJ7AQ/original')
```tsx
layer.color('rate', ['#ffffcc', '#b6e2b6', '#64c1c0', '#338cbb', '#253494'])
```
对于连续型
数据 L7 默认会设置为 linear, domain为数据的min、max值。颜色会根据 range 颜色生成渐变色。linear
默认可不设置 domain 区间
```
layer.scale('value', {
type: 'linear',
domain: [5, 100], // 可定义domain,也可以不设置,自定根据数据计算
});
```
## quantize 连续等间距
等间距分类会根据属性值范围划分为若干个大小相等的子范围。相等间隔最适用于常见的数据范围,如百分比和温度。这种方法强调的是某个属性值相对于其他值的量。
![L7 Scale 连续等间距](https://mdn.alipayobjects.com/huamei_qa8qxu/afts/img/A*YmwwQ5L-d7QAAAAAAAAAAAAADmJ7AQ/original)
等间距分类只与数据 mix、max 有关,如果没有设置 domain,将自动计算数据的 min、max 进行分段,分段个数依据 range 颜色的个数。等间距会出现空类,而且每个分类要素分布不均匀。
```tsx
layer.color('type', ['red', 'white', 'blue', 'yellow']);
```
你也可以自定义 domain
```ts
layer.scale('value', {
type: 'quantize',
domain: [5, 100],
});
layer.color('type', ['red', 'white', 'blue', 'yellow']);
```
![L7 scale quantize](https://mdn.alipayobjects.com/huamei_qa8qxu/afts/img/A*N61_Q6-U7jIAAAAAAAAAAAAADmJ7AQ/original)
## quantile 连续等分位
等分位要求每个分类区间都含有相等数量的要素。分位数为每个分类分配相等数量数据值,不存在空类,也不存在值过多或过少的类。
![L7 scale quantile](https://mdn.alipayobjects.com/huamei_qa8qxu/afts/img/A*2SFpSLRD3yYAAAAAAAAAAAAADmJ7AQ/original)
等分位与数据的分布相关,需要拿到全量数据才能计算,因此等分位的 scale 不能单独设置 domain只能自动计算。
```tsx
layer.scale('value', {
type: 'quantile',
});
layer.color('value', ['red', 'white', 'blue', 'yellow']);
```
![L7 Scale quantile](https://mdn.alipayobjects.com/huamei_qa8qxu/afts/img/A*EMcjSrYe1l0AAAAAAAAAAAAADmJ7AQ/original)
## threshold 自定义分类
自定义分类可以设定任意分类区间映射到对于视觉变量。domain 仍然是连续的并根据提供的domain 进行分类。range 属性必须有 N+1 个元素,其中 N 是 domain 的个数。
threshold 为自定义分段,在使用时必须为 Scale 设置 domain, domain可以依据需求任意设置。
```tsx
layer.scale('rate', {
type: 'threshold',
domain: [3, 6, 8, 10],
})
.color('rate', ['#ffffcc', '#b6e2b6', '#64c1c0', '#338cbb', '#253494'])
```
![ L7 Scale threshold](https://mdn.alipayobjects.com/huamei_qa8qxu/afts/img/A*e1YyRKELsjwAAAAAAAAAAAAADmJ7AQ/original)
## diverging 离散分类
离散分类通常与两种相反的色调一起使用,以显示从负值到中心到正值的变化。这些类型的地图显示了彼此相关的值的大小。
![L7 diverging](https://mdn.alipayobjects.com/huamei_qa8qxu/afts/img/A*8anRRLJNu6YAAAAAAAAAAAAADmJ7AQ/original)
diverging 如果没有设置 domain 会自动根据数据计算min、middle、max 三个值作为domain。range 至少需要设置三个颜色,形成双极色带。
```ts
layer.scale('rate', {
type: 'diverging'
domain: [3, 6, 8, 10], // the input domain and output range of a diverging scal
})
.color('rate', ['#ffffcc', '#b6e2b6', '#64c1c0', '#338cbb', '#253494'])
```
![L7 scale diverging](https://mdn.alipayobjects.com/huamei_qa8qxu/afts/img/A*4rcDQIRdRdEAAAAAAAAAAAAADmJ7AQ/original)