mirror of https://gitee.com/antv-l7/antv-l7
chore: scale 升级优化 (#1126)
* fix: 添加core Layer inteface export & 增加 scale demo * fix: 添加core Layer inteface export & 增加 scale demo * chore: 优化数据映射模块 * feat(scale): dIVERGING 支持设置中间值
This commit is contained in:
parent
6f6396f0cd
commit
b82e4c250a
|
@ -16,6 +16,7 @@ import { ILayer } from './ILayerService';
|
|||
|
||||
export enum ScaleTypes {
|
||||
LINEAR = 'linear',
|
||||
SEQUENTIAL = 'sequential',
|
||||
POWER = 'power',
|
||||
LOG = 'log',
|
||||
IDENTITY = 'identity',
|
||||
|
@ -24,6 +25,7 @@ export enum ScaleTypes {
|
|||
QUANTIZE = 'quantize',
|
||||
THRESHOLD = 'threshold',
|
||||
CAT = 'cat',
|
||||
DIVERGING = 'diverging',
|
||||
}
|
||||
export type ScaleTypeName =
|
||||
| 'linear'
|
||||
|
@ -34,30 +36,30 @@ export type ScaleTypeName =
|
|||
| 'quantile'
|
||||
| 'quantize'
|
||||
| 'threshold'
|
||||
| 'diverging'
|
||||
| 'sequential'
|
||||
| 'cat';
|
||||
|
||||
export type ScaleAttributeType = 'color' | 'size' | 'shape';
|
||||
export interface IScale {
|
||||
type: ScaleTypeName;
|
||||
neutral?: number;
|
||||
field?: string;
|
||||
unknown?: string;
|
||||
ticks?: any[];
|
||||
nice?: boolean;
|
||||
clamp?: boolean;
|
||||
format?: () => any;
|
||||
domain?: any[];
|
||||
range?: any[];
|
||||
}
|
||||
|
||||
export enum StyleScaleType {
|
||||
CONSTANT = 'constant',
|
||||
VARIABLE = 'variable',
|
||||
}
|
||||
export interface IScaleOption {
|
||||
field?: string;
|
||||
export interface IScaleOption extends IScale {
|
||||
attr?: ScaleAttributeType;
|
||||
type: ScaleTypeName;
|
||||
ticks?: any[];
|
||||
nice?: boolean;
|
||||
format?: () => any;
|
||||
domain?: any[];
|
||||
}
|
||||
export interface IScaleOptions {
|
||||
[key: string]: IScale | undefined;
|
||||
|
|
|
@ -81,7 +81,7 @@ export default class StyleAttribute implements IStyleAttribute {
|
|||
}
|
||||
return params.map((param, idx) => {
|
||||
const scaleFunc = this.scale?.scalers![idx].func;
|
||||
// @ts-ignore
|
||||
// @ts-ignore // TODO 支持双变量映射
|
||||
const value = scaleFunc(param);
|
||||
return value;
|
||||
});
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
"d3-array": "1",
|
||||
"d3-color": "^1.4.0",
|
||||
"d3-scale": "2",
|
||||
"d3-interpolate":"2",
|
||||
"earcut": "^2.2.1",
|
||||
"eventemitter3": "^4.0.0",
|
||||
"extrude-polyline": "^1.0.6",
|
||||
|
@ -47,6 +48,7 @@
|
|||
"@types/d3-array": "^2.0.0",
|
||||
"@types/d3-color": "^1.2.2",
|
||||
"@types/d3-scale": "^2.1.1",
|
||||
"@types/d3-interpolate":"2.0.2",
|
||||
"@types/earcut": "^2.1.0",
|
||||
"@types/gl-matrix": "^2.4.5",
|
||||
"@types/lodash": "^4.14.138"
|
||||
|
|
|
@ -14,6 +14,7 @@ import {
|
|||
} from '@antv/l7-core';
|
||||
import { IParseDataItem } from '@antv/l7-source';
|
||||
import { extent, ticks } from 'd3-array';
|
||||
import * as d3interpolate from 'd3-interpolate';
|
||||
import * as d3 from 'd3-scale';
|
||||
import { inject, injectable } from 'inversify';
|
||||
import { isNil, isString, uniq } from 'lodash';
|
||||
|
@ -26,11 +27,13 @@ const scaleMap = {
|
|||
[ScaleTypes.POWER]: d3.scalePow,
|
||||
[ScaleTypes.LOG]: d3.scaleLog,
|
||||
[ScaleTypes.IDENTITY]: d3.scaleIdentity,
|
||||
[ScaleTypes.SEQUENTIAL]: d3.scaleSequential,
|
||||
[ScaleTypes.TIME]: d3.scaleTime,
|
||||
[ScaleTypes.QUANTILE]: d3.scaleQuantile,
|
||||
[ScaleTypes.QUANTIZE]: d3.scaleQuantize,
|
||||
[ScaleTypes.THRESHOLD]: d3.scaleThreshold,
|
||||
[ScaleTypes.CAT]: d3.scaleOrdinal,
|
||||
[ScaleTypes.DIVERGING]: d3.scaleDiverging,
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -109,32 +112,42 @@ export default class FeatureScalePlugin implements ILayerPlugin {
|
|||
const type = attribute.name;
|
||||
attributeScale.names = this.parseFields(attribute!.scale!.field || []);
|
||||
const scales: IStyleScale[] = [];
|
||||
// 为每个字段创建 Scale
|
||||
attributeScale.names.forEach((field: string | number) => {
|
||||
scales.push(this.getOrCreateScale(field, attribute, dataArray));
|
||||
scales.push(
|
||||
this.createScale(
|
||||
field,
|
||||
attribute.name,
|
||||
attribute.scale?.values,
|
||||
dataArray,
|
||||
),
|
||||
);
|
||||
});
|
||||
|
||||
// 为scales 设置值区间
|
||||
// 为scales 设置值区间 Range
|
||||
if (scales.some((scale) => scale.type === StyleScaleType.VARIABLE)) {
|
||||
attributeScale.type = StyleScaleType.VARIABLE;
|
||||
scales.forEach((scale) => {
|
||||
// 如果设置了回调, 这不需要设置让range
|
||||
// 如果设置了回调, 这不需要设置range
|
||||
if (!attributeScale.callback) {
|
||||
if (attributeScale.values && attributeScale.values !== 'text') {
|
||||
if (
|
||||
scale.option?.type === 'linear' &&
|
||||
attributeScale.values.length > 2
|
||||
) {
|
||||
const tick = scale.scale.ticks(attributeScale.values.length);
|
||||
if (type === 'color') {
|
||||
// TODO: 这里改变了值域,获取图例的时候有问题
|
||||
scale.scale.domain(tick);
|
||||
}
|
||||
}
|
||||
if (
|
||||
attributeScale.values &&
|
||||
attributeScale.values !== 'text' &&
|
||||
scale.option?.type !== ScaleTypes.DIVERGING &&
|
||||
scale.option?.type !== ScaleTypes.SEQUENTIAL
|
||||
) {
|
||||
scale.scale.range(attributeScale.values); // 判断常量, 默认值
|
||||
} else if (scale.option?.type === 'cat') {
|
||||
} else if (scale.option?.type === ScaleTypes.CAT) {
|
||||
// 如果没有设置初值且 类型为cat,range ==domain;
|
||||
|
||||
scale.scale.range(scale.option.domain);
|
||||
} else if (
|
||||
scale.option?.type === ScaleTypes.DIVERGING ||
|
||||
scale.option?.type === ScaleTypes.SEQUENTIAL
|
||||
) {
|
||||
scale.scale.interpolator(
|
||||
// @ts-ignore
|
||||
d3interpolate.interpolateRgbBasis(attributeScale.values),
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -157,25 +170,6 @@ export default class FeatureScalePlugin implements ILayerPlugin {
|
|||
}
|
||||
});
|
||||
}
|
||||
private getOrCreateScale(
|
||||
field: string | number,
|
||||
attribute: IStyleAttribute,
|
||||
dataArray: IParseDataItem[],
|
||||
) {
|
||||
const scalekey = [field, attribute.name].join('_');
|
||||
const values = attribute.scale?.values;
|
||||
// if (this.scaleCache[scalekey]) {
|
||||
// return this.scaleCache[scalekey];
|
||||
// }
|
||||
const styleScale = this.createScale(
|
||||
field,
|
||||
attribute.name,
|
||||
values,
|
||||
dataArray,
|
||||
);
|
||||
// this.scaleCache[scalekey] = styleScale;
|
||||
return styleScale;
|
||||
}
|
||||
|
||||
/**
|
||||
* @example
|
||||
|
@ -203,7 +197,7 @@ export default class FeatureScalePlugin implements ILayerPlugin {
|
|||
// scale 支持根据视觉通道和字段
|
||||
const scaleOption: IScale | undefined =
|
||||
this.scaleOptions[name] && this.scaleOptions[name]?.field === field
|
||||
? this.scaleOptions[name]
|
||||
? this.scaleOptions[name] // TODO zi
|
||||
: this.scaleOptions[field];
|
||||
const styleScale: IStyleScale = {
|
||||
field,
|
||||
|
@ -234,8 +228,8 @@ export default class FeatureScalePlugin implements ILayerPlugin {
|
|||
// text 为内置变 如果是文本则为cat
|
||||
type = ScaleTypes.CAT;
|
||||
}
|
||||
const cfg = this.createDefaultScaleConfig(type, field, data);
|
||||
Object.assign(cfg, scaleOption);
|
||||
const cfg = this.createScaleConfig(type, field, scaleOption, data);
|
||||
|
||||
styleScale.scale = this.createDefaultScale(cfg);
|
||||
styleScale.option = cfg;
|
||||
}
|
||||
|
@ -249,33 +243,59 @@ export default class FeatureScalePlugin implements ILayerPlugin {
|
|||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
private createDefaultScaleConfig(
|
||||
// 生成Scale 默认配置
|
||||
private createScaleConfig(
|
||||
type: ScaleTypeName,
|
||||
field: string | number,
|
||||
scaleOption: IScale | undefined,
|
||||
data?: IParseDataItem[],
|
||||
) {
|
||||
const cfg: IScale = {
|
||||
type,
|
||||
};
|
||||
const values = data?.map((item) => item[field]) || [];
|
||||
if (scaleOption?.domain) {
|
||||
cfg.domain = scaleOption?.domain;
|
||||
}
|
||||
// 默认类型为 Quantile Scales https://github.com/d3/d3-scale#quantile-scales
|
||||
if (type !== ScaleTypes.CAT && type !== ScaleTypes.QUANTILE) {
|
||||
else if (
|
||||
type !== ScaleTypes.CAT &&
|
||||
type !== ScaleTypes.QUANTILE &&
|
||||
type !== ScaleTypes.DIVERGING
|
||||
) {
|
||||
// linear/
|
||||
cfg.domain = extent(values);
|
||||
} else if (type === ScaleTypes.CAT) {
|
||||
cfg.domain = uniq(values);
|
||||
} else if (type === ScaleTypes.QUANTILE) {
|
||||
cfg.domain = values;
|
||||
} else if (type === ScaleTypes.DIVERGING) {
|
||||
const minMax = extent(values);
|
||||
const neutral =
|
||||
scaleOption?.neutral !== undefined
|
||||
? scaleOption?.neutral
|
||||
: (minMax[0] + minMax[1]) / 2;
|
||||
cfg.domain = [minMax[0], neutral, minMax[1]];
|
||||
}
|
||||
return cfg;
|
||||
return { ...cfg, ...scaleOption };
|
||||
}
|
||||
|
||||
private createDefaultScale({ type, domain }: IScale) {
|
||||
// 创建Scale 实例
|
||||
private createDefaultScale({ type, domain, unknown, clamp, nice }: IScale) {
|
||||
// @ts-ignore
|
||||
const scale = scaleMap[type]();
|
||||
if (domain) {
|
||||
if (domain && scale.domain) {
|
||||
scale.domain(domain);
|
||||
}
|
||||
if (unknown) {
|
||||
scale.unknown(unknown);
|
||||
}
|
||||
if (clamp !== undefined && scale.clamp) {
|
||||
scale.clamp(clamp);
|
||||
}
|
||||
if (nice !== undefined && scale.nice) {
|
||||
scale.nice(nice);
|
||||
}
|
||||
// TODO 其他属性支持
|
||||
return scale;
|
||||
}
|
||||
|
|
|
@ -3,8 +3,10 @@ import * as React from 'react';
|
|||
import Quantize from './components/Quantize';
|
||||
import PointScale from './components/Point';
|
||||
import Threshold from './components/Threshold';
|
||||
import Diverging from './components/Diverging';
|
||||
|
||||
storiesOf('数据映射', module)
|
||||
.add('枚举类型', () => <PointScale />)
|
||||
.add('颜色范围等分', () => <Quantize />)
|
||||
.add('离散', () => <Diverging />)
|
||||
.add('自定义范围', () => <Threshold />);
|
||||
|
|
|
@ -0,0 +1,351 @@
|
|||
import { PolygonLayer, Scene, Popup } from '@antv/l7';
|
||||
import { GaodeMap } from '@antv/l7-maps';
|
||||
import * as React from 'react';
|
||||
|
||||
const list = [
|
||||
{
|
||||
value: 10.0,
|
||||
color1: 'red',
|
||||
province_adcode: '110000',
|
||||
province_adName: '北京市',
|
||||
province: '北京市',
|
||||
},
|
||||
{
|
||||
value: 8.0,
|
||||
color1: 'red',
|
||||
province_adcode: '120000',
|
||||
province_adName: '天津市',
|
||||
province: '天津市',
|
||||
},
|
||||
{
|
||||
value: 6.0,
|
||||
color1: 'blue',
|
||||
province_adcode: '130000',
|
||||
province_adName: '河北省',
|
||||
province: '河北省',
|
||||
},
|
||||
{
|
||||
value: 4.0,
|
||||
color1: 'blue',
|
||||
province_adcode: '140000',
|
||||
province_adName: '山西省',
|
||||
province: '山西省',
|
||||
},
|
||||
{
|
||||
value: 2.0,
|
||||
color1: 'blue',
|
||||
province_adcode: '150000',
|
||||
province_adName: '内蒙古自治区',
|
||||
province: '内蒙古自治区',
|
||||
},
|
||||
{
|
||||
value: 0.0,
|
||||
color1: 'blue',
|
||||
province_adcode: '210000',
|
||||
province_adName: '辽宁省',
|
||||
province: '辽宁省',
|
||||
},
|
||||
{
|
||||
value: -2,
|
||||
color1: 'green',
|
||||
province_adcode: '220000',
|
||||
province_adName: '吉林省',
|
||||
province: '吉林省',
|
||||
},
|
||||
{
|
||||
value: -4,
|
||||
color1: 'green',
|
||||
province_adcode: '230000',
|
||||
province_adName: '黑龙江省',
|
||||
province: '黑龙江省',
|
||||
},
|
||||
{
|
||||
value: -6,
|
||||
color1: 'green',
|
||||
province_adcode: '310000',
|
||||
province_adName: '上海市',
|
||||
province: '上海市',
|
||||
},
|
||||
{
|
||||
value: -8,
|
||||
color1: 'green',
|
||||
province_adcode: '320000',
|
||||
province_adName: '江苏省',
|
||||
province: '江苏省',
|
||||
},
|
||||
{
|
||||
value: -10,
|
||||
color1: 'green',
|
||||
province_adcode: '330000',
|
||||
province_adName: '浙江省',
|
||||
province: '浙江省',
|
||||
},
|
||||
{
|
||||
value: 12.0,
|
||||
color1: 'yellow',
|
||||
province_adcode: '340000',
|
||||
province_adName: '安徽省',
|
||||
province: '安徽省',
|
||||
},
|
||||
{
|
||||
value: -12,
|
||||
color1: 'yellow',
|
||||
province_adcode: '350000',
|
||||
province_adName: '福建省',
|
||||
province: '福建省',
|
||||
},
|
||||
{
|
||||
value: 14.0,
|
||||
color1: 'yellow',
|
||||
province_adcode: '360000',
|
||||
province_adName: '江西省',
|
||||
province: '江西省',
|
||||
},
|
||||
{
|
||||
value: -14.0,
|
||||
color1: 'yellow',
|
||||
province_adcode: '370000',
|
||||
province_adName: '山东省',
|
||||
province: '山东省',
|
||||
},
|
||||
{
|
||||
value: 16.0,
|
||||
color1: 'yellow',
|
||||
province_adcode: '410000',
|
||||
province_adName: '河南省',
|
||||
province: '河南省',
|
||||
},
|
||||
{
|
||||
value: -16,
|
||||
color1: 'yellow',
|
||||
province_adcode: '420000',
|
||||
province_adName: '湖北省',
|
||||
province: '湖北省',
|
||||
},
|
||||
{
|
||||
value: 18.0,
|
||||
color1: 'yellow',
|
||||
province_adcode: '430000',
|
||||
province_adName: '湖南省',
|
||||
province: '湖南省',
|
||||
},
|
||||
{
|
||||
value: -18.0,
|
||||
color1: 'yellow',
|
||||
province_adcode: '440000',
|
||||
province_adName: '广东省',
|
||||
province: '广东省',
|
||||
},
|
||||
{
|
||||
value: 20.0,
|
||||
color1: 'yellow',
|
||||
province_adcode: '450000',
|
||||
province_adName: '广西壮族自治区',
|
||||
province: '广西壮族自治区',
|
||||
},
|
||||
{
|
||||
value: 21.0,
|
||||
color1: 'orange',
|
||||
province_adcode: '460000',
|
||||
province_adName: '海南省',
|
||||
province: '海南省',
|
||||
},
|
||||
{
|
||||
value: 22.0,
|
||||
color1: 'orange',
|
||||
province_adcode: '500000',
|
||||
province_adName: '重庆市',
|
||||
province: '重庆市',
|
||||
},
|
||||
{
|
||||
value: 23.0,
|
||||
color1: 'orange',
|
||||
province_adcode: '510000',
|
||||
province_adName: '四川省',
|
||||
province: '四川省',
|
||||
},
|
||||
{
|
||||
value: 24.0,
|
||||
color1: 'orange',
|
||||
province_adcode: '520000',
|
||||
province_adName: '贵州省',
|
||||
province: '贵州省',
|
||||
},
|
||||
{
|
||||
value: 25.0,
|
||||
color1: 'orange',
|
||||
province_adcode: '530000',
|
||||
province_adName: '云南省',
|
||||
province: '云南省',
|
||||
},
|
||||
{
|
||||
value: -26.0,
|
||||
color1: 'orange',
|
||||
province_adcode: '540000',
|
||||
province_adName: '西藏自治区',
|
||||
province: '西藏自治区',
|
||||
},
|
||||
{
|
||||
value: 27.0,
|
||||
color1: 'orange',
|
||||
province_adcode: '610000',
|
||||
province_adName: '陕西省',
|
||||
province: '陕西省',
|
||||
},
|
||||
{
|
||||
value: -28.0,
|
||||
color1: 'orange',
|
||||
province_adcode: '630000',
|
||||
province_adName: '青海省',
|
||||
province: '青海省',
|
||||
},
|
||||
{
|
||||
value: 29.0,
|
||||
color1: 'orange',
|
||||
province_adcode: '640000',
|
||||
province_adName: '宁夏回族自治区',
|
||||
province: '宁夏回族自治区',
|
||||
},
|
||||
{
|
||||
value: 60.0,
|
||||
color1: 'orange',
|
||||
province_adcode: '650000',
|
||||
province_adName: '新疆维吾尔自治区',
|
||||
province: '新疆维吾尔自治区',
|
||||
},
|
||||
{
|
||||
value: -31.0,
|
||||
color1: 'orange',
|
||||
province_adcode: '710000',
|
||||
province_adName: '台湾省',
|
||||
province: '台湾省',
|
||||
},
|
||||
{
|
||||
value: 80.0,
|
||||
color1: 'orange',
|
||||
province_adcode: '810000',
|
||||
province_adName: '香港特别行政区',
|
||||
province: '香港特别行政区',
|
||||
},
|
||||
{
|
||||
value: -33.0,
|
||||
color1: 'orange',
|
||||
province_adcode: '820000',
|
||||
province_adName: '澳门特别行政区',
|
||||
province: '澳门特别行政区',
|
||||
},
|
||||
];
|
||||
|
||||
export default class Diverging extends React.Component {
|
||||
private scene: Scene;
|
||||
|
||||
public componentWillUnmount() {
|
||||
this.scene.destroy();
|
||||
}
|
||||
|
||||
public async componentDidMount() {
|
||||
const scene = new Scene({
|
||||
id: 'map',
|
||||
pickBufferScale: 1.0,
|
||||
map: new GaodeMap({
|
||||
style: 'light',
|
||||
center: [-121.24357, 37.58264],
|
||||
pitch: 0,
|
||||
zoom: 6.45,
|
||||
}),
|
||||
});
|
||||
scene.on('loaded', () => {
|
||||
fetch(
|
||||
'https://gw.alipayobjects.com/os/alisis/geo-data-v0.1.1/choropleth-data/country/100000_country_province.json',
|
||||
)
|
||||
.then((res) => res.json())
|
||||
.then((data) => {
|
||||
const chinaPolygonLayer = new PolygonLayer({
|
||||
autoFit: true,
|
||||
})
|
||||
.source(data, {
|
||||
transforms: [
|
||||
{
|
||||
type: 'join',
|
||||
sourceField: 'province_adName',
|
||||
targetField: 'name', // data 对应字段名 绑定到的地理数据
|
||||
data: list,
|
||||
},
|
||||
],
|
||||
})
|
||||
.scale({
|
||||
value: {
|
||||
ticks: [5],
|
||||
type: 'linear', //sequential
|
||||
// domain: [-40, 0, 40],
|
||||
},
|
||||
})
|
||||
.color('value', [
|
||||
'#eff3ff',
|
||||
'#bdd7e7',
|
||||
'#6baed6',
|
||||
'#3182bd',
|
||||
'#08519c',
|
||||
])
|
||||
.shape('fill')
|
||||
.style({
|
||||
opacity: 1,
|
||||
});
|
||||
|
||||
const textLayer = new PolygonLayer({
|
||||
autoFit: false,
|
||||
})
|
||||
.source(data, {
|
||||
transforms: [
|
||||
{
|
||||
type: 'join',
|
||||
sourceField: 'province_adName',
|
||||
targetField: 'name', // data 对应字段名 绑定到的地理数据
|
||||
data: list,
|
||||
},
|
||||
],
|
||||
})
|
||||
.size(10)
|
||||
.color('#000')
|
||||
.shape('value', 'text');
|
||||
|
||||
chinaPolygonLayer.on('add', (type) => {
|
||||
console.log(
|
||||
'getLegendItems: ',
|
||||
chinaPolygonLayer.getLegendItems('color'),
|
||||
);
|
||||
});
|
||||
|
||||
chinaPolygonLayer.on('mousemove', (e) => {
|
||||
const popup = new Popup({
|
||||
offsets: [0, 0],
|
||||
closeButton: false,
|
||||
})
|
||||
.setLnglat(e.lngLat)
|
||||
.setHTML(e.feature.properties.name + e.feature.properties.value);
|
||||
scene.addPopup(popup);
|
||||
});
|
||||
|
||||
scene.addLayer(chinaPolygonLayer);
|
||||
scene.addLayer(textLayer);
|
||||
});
|
||||
|
||||
this.scene = scene;
|
||||
});
|
||||
}
|
||||
|
||||
public render() {
|
||||
return (
|
||||
<div
|
||||
id="map"
|
||||
style={{
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
|
@ -76,6 +76,7 @@ export default class PointScale extends React.Component {
|
|||
.scale({
|
||||
type: {
|
||||
type: 'cat',
|
||||
unknown: '#eee',
|
||||
domain: ['C', 'B', 'A'],
|
||||
},
|
||||
})
|
||||
|
|
Loading…
Reference in New Issue