feat: scale 支持根据字段和颜色指定

This commit is contained in:
thinkinggis 2020-02-18 23:23:28 +08:00
parent 5936f9aa47
commit c19df8344f
14 changed files with 138 additions and 65 deletions

View File

@ -22,6 +22,7 @@ import {
IScale,
IScaleOptions,
IStyleAttributeService,
ScaleAttributeType,
StyleAttrField,
StyleAttributeOption,
Triangulation,
@ -118,7 +119,7 @@ export interface ILayer {
Partial<IModelInitializationOptions>,
): IModel;
init(): ILayer;
scale(field: string | IScaleOptions, cfg?: IScale): ILayer;
scale(field: string | number | IScaleOptions, cfg?: IScale): ILayer;
size(field: StyleAttrField, value?: StyleAttributeOption): ILayer;
color(field: StyleAttrField, value?: StyleAttributeOption): ILayer;
shape(field: StyleAttrField, value?: StyleAttributeOption): ILayer;
@ -139,6 +140,7 @@ export interface ILayer {
style(options: unknown): ILayer;
hide(): ILayer;
show(): ILayer;
getLegendItems(name: string): any;
setIndex(index: number): ILayer;
isVisible(): boolean;
setMaxZoom(min: number): ILayer;

View File

@ -35,8 +35,11 @@ export type ScaleTypeName =
| 'quantize'
| 'threshold'
| 'cat';
export type ScaleAttributeType = 'color' | 'size' | 'shape';
export interface IScale {
type: ScaleTypeName;
field?: string;
ticks?: any[];
nice?: boolean;
format?: () => any;
@ -49,6 +52,7 @@ export enum StyleScaleType {
}
export interface IScaleOption {
field?: string;
attr?: ScaleAttributeType;
type: ScaleTypeName;
ticks?: any[];
nice?: boolean;
@ -115,6 +119,11 @@ type CallBack = (...args: any[]) => any;
export type StyleAttributeField = string | string[] | number[];
export type StyleAttributeOption = string | number | boolean | any[] | CallBack;
export type StyleAttrField = string | string[] | number | number[];
export interface IAttributeScale {
field: string | number;
func: unknown;
option: IScaleOption | undefined;
}
export interface IStyleAttributeInitializationOptions {
name: string;
@ -125,10 +134,7 @@ export interface IStyleAttributeInitializationOptions {
names: string[] | number[];
type: StyleScaleType;
callback?: (...args: any[]) => [];
scalers?: Array<{
field: string | number;
func: unknown;
}>;
scalers?: IAttributeScale[];
};
descriptor: IVertexAttributeDescriptor;
}
@ -186,6 +192,7 @@ export interface IStyleAttributeService {
): void;
getLayerStyleAttributes(): IStyleAttribute[] | undefined;
getLayerStyleAttribute(attributeName: string): IStyleAttribute | undefined;
getLayerAttributeScale(attributeName: string): any;
createAttributesAndIndices(
encodedFeatures: IEncodeFeature[],
triangulation?: Triangulation,

View File

@ -1,5 +1,7 @@
import { isNil } from 'lodash';
import {
IAttributeScale,
IScaleOption,
IStyleAttribute,
StyleScaleType,
} from '../layer/IStyleAttributeService';
@ -22,10 +24,7 @@ export default class StyleAttribute implements IStyleAttribute {
field: string | string[];
values: unknown[];
callback?: (...args: any[]) => [];
scalers?: Array<{
field: string;
func: unknown;
}>;
scalers?: IAttributeScale[];
};
public descriptor: IVertexAttributeDescriptor;
public featureBufferLayout: Array<{

View File

@ -7,6 +7,7 @@ import { IRendererService } from '../renderer/IRendererService';
import { IParseDataItem } from '../source/ISourceService';
import { ILayer } from './ILayerService';
import {
IAttributeScale,
IEncodeFeature,
IStyleAttribute,
IStyleAttributeInitializationOptions,
@ -108,6 +109,15 @@ export default class StyleAttributeService implements IStyleAttributeService {
);
}
public getLayerAttributeScale(name: string) {
const attribute = this.getLayerStyleAttribute(name);
const scale = attribute?.scale?.scalers as IAttributeScale[];
if (scale && scale[0]) {
return scale[0].func;
}
return null;
}
public updateAttributeByFeatureRange(
attributeName: string,
features: IEncodeFeature[],

View File

@ -31,6 +31,7 @@ import {
IStyleAttributeService,
IStyleAttributeUpdateOptions,
lazyInject,
ScaleAttributeType,
ScaleTypeName,
ScaleTypes,
StyleAttributeField,
@ -461,7 +462,7 @@ export default class BaseLayer<ChildLayerStyleOptions = {}> extends EventEmitter
}
return this;
}
public scale(field: ScaleTypeName | IScaleOptions, cfg: IScale) {
public scale(field: string | IScaleOptions, cfg: IScale) {
if (isObject(field)) {
this.scaleOptions = {
...this.scaleOptions,
@ -748,6 +749,30 @@ export default class BaseLayer<ChildLayerStyleOptions = {}> extends EventEmitter
}
return this.configSchema;
}
public getLegendItems(name: string) {
const scale = this.styleAttributeService.getLayerAttributeScale(name);
if (scale) {
if (scale.ticks) {
const items = scale.ticks().map((item: any) => {
return {
value: item,
[name]: scale(item),
};
});
return items;
} else if (scale.invertExtent) {
const items = scale.range().map((item: any) => {
return {
value: scale.invertExtent(item),
[name]: item,
};
});
return items;
}
} else {
return [];
}
}
public pick({ x, y }: { x: number; y: number }) {
this.interactionService.triggerHover({ x, y });

View File

@ -64,7 +64,7 @@ export default class FeatureScalePlugin implements ILayerPlugin {
this.caculateScalesForAttributes(attributes || [], dataArray);
});
// 检测数据是否需要更新
// 检测数据是否需要更新
layer.hooks.beforeRenderData.tap('FeatureScalePlugin', (flag) => {
if (flag) {
this.scaleOptions = layer.getScaleOptions();
@ -146,6 +146,7 @@ export default class FeatureScalePlugin implements ILayerPlugin {
return {
field: scale.field,
func: scale.scale,
option: scale.option,
};
});
@ -160,13 +161,17 @@ export default class FeatureScalePlugin implements ILayerPlugin {
) {
const scalekey = [field, attribute.name].join('_');
const values = attribute.scale?.values;
if (this.scaleCache[scalekey]) {
return this.scaleCache[scalekey];
}
const styleScale = this.createScale(field, values, dataArray);
this.scaleCache[scalekey] = styleScale;
return this.scaleCache[scalekey];
// if (this.scaleCache[scalekey]) {
// return this.scaleCache[scalekey];
// }
const styleScale = this.createScale(
field,
attribute.name,
values,
dataArray,
);
// this.scaleCache[scalekey] = styleScale;
return styleScale;
}
/**
@ -188,11 +193,15 @@ export default class FeatureScalePlugin implements ILayerPlugin {
private createScale(
field: string | number,
name: string,
values: unknown[] | string | undefined,
data?: IParseDataItem[],
): IStyleScale {
// 首先查找全局默认配置例如 color
const scaleOption: IScale | undefined = this.scaleOptions[field];
// scale 支持根据视觉通道和字段
const scaleOption: IScale | undefined =
this.scaleOptions[name] && this.scaleOptions[name].field === field
? this.scaleOptions[name]
: this.scaleOptions[field];
const styleScale: IStyleScale = {
field,
scale: undefined,

View File

@ -1,5 +1,5 @@
import { IMapConfig, Scene } from '@antv/l7';
// @ts-ignore
// tslint:disable-next-line:no-submodule-imports
import GaodeMap from '@antv/l7-maps/lib/amap';
import React, { createElement, createRef, useEffect, useState } from 'react';

View File

@ -11,8 +11,9 @@ export default React.memo(function Chart(props: ILayerProps) {
const { layer, color } = props;
useEffect(() => {
color.field
? layer.color(color.field as StyleAttrField, color.values)
? layer.color(color.field as StyleAttrField, color.value)
: layer.color(color.value as StyleAttrField);
}, [color.value, color.field, JSON.stringify(color.values)]);
}, [color.field, color.scale, JSON.stringify(color.value)]);
return null;
});

View File

@ -1,12 +1,12 @@
import { ILayer, LineLayer, PointLayer, PolygonLayer, Scene } from '@antv/l7';
import * as React from 'react';
import { useSceneValue } from '../SceneContext';
import { Color, ILayerProps, Scales, Shape, Size, Source, Style } from './';
import { Color, ILayerProps, Scale, Shape, Size, Source, Style } from './';
const { useEffect, useState } = React;
export default function BaseLayer(type: string, props: ILayerProps) {
const { source, color, shape, style, size, scales, options } = props;
const { source, color, shape, style, size, scale, options } = props;
const mapScene = (useSceneValue() as unknown) as Scene;
const [layer, setLayer] = useState();
if (!layer) {
@ -41,7 +41,7 @@ export default function BaseLayer(type: string, props: ILayerProps) {
return (
<>
<Source layer={layer} source={source} />
{scales && <Scales layer={layer} scales={scales} />}
{scale && <Scale layer={layer} scale={scale} />}
<Color layer={layer} color={color} />
{size && <Size layer={layer} size={size} />}
<Shape layer={layer} shape={shape} />

View File

@ -5,14 +5,14 @@ import { IScaleAttributeOptions } from './';
const { useEffect } = React;
interface ILayerProps {
layer: ILayer;
scales: Partial<IScaleAttributeOptions>;
scale: Partial<IScaleAttributeOptions>;
}
export default React.memo(function Chart(props: ILayerProps) {
const { layer, scales } = props;
const { layer, scale } = props;
useEffect(() => {
scales.field
? layer.scale(scales.field as string, scales.value as IScale)
: layer.scale(scales.values as IScaleOptions);
}, [scales.value, scales.field, JSON.stringify(scales.values)]);
scale.value
? layer.scale(scale.field as string, scale.value as IScale)
: layer.scale(scale.field as IScaleOptions);
}, [scale.value, scale.field]);
return null;
});

View File

@ -13,6 +13,6 @@ export default React.memo(function Chart(props: ILayerProps) {
size.field
? layer.size(size.field, size.values)
: layer.size(size.value as StyleAttrField);
}, [size.field, size.value, JSON.stringify(size.values)]);
}, [size.field, size.value, size.scale, size.values]);
return null;
});

View File

@ -1,6 +1,11 @@
import { IScale, IScaleOptions, ISourceCFG } from '@antv/l7';
import {
IScale,
IScaleOptions,
ISourceCFG,
ScaleAttributeType,
} from '@antv/l7';
import Color from './Color';
import Scales from './Scales';
import Scale from './Scale';
import Shape from './Shape';
import Size from './Size';
import Source from './Source';
@ -9,13 +14,18 @@ export interface IAttributeOptions {
field: string;
value: string | number;
values: string[] | number[] | string;
scale?: string;
}
export interface IScaleAttributeOptions {
field: string;
field: string | IScaleOptions;
value: IScale;
values: IScaleOptions;
}
export interface IScaleOption {
[key: string]: IScaleAttributeOptions;
}
export interface IStyleOptions {
opacity: number;
[key: string]: any;
@ -30,9 +40,9 @@ export interface ILayerProps {
source: ISourceOptions;
color: Partial<IAttributeOptions>;
shape: Partial<IAttributeOptions>;
scales?: Partial<IScaleAttributeOptions>;
scale?: Partial<IScaleAttributeOptions>;
size?: Partial<IAttributeOptions>;
style?: Partial<IStyleOptions>;
}
export { Source, Size, Color, Shape, Style, Scales };
export { Source, Size, Color, Shape, Style, Scale };

View File

@ -1,5 +1,5 @@
import { IMapConfig, Scene } from '@antv/l7';
// @ts-ignore
// tslint:disable-next-line:no-submodule-imports
import Mapbox from '@antv/l7-maps/lib/mapbox';
import React, { createElement, createRef, useEffect, useState } from 'react';

View File

@ -19,40 +19,50 @@ export default class Point3D extends React.Component {
const scene = new Scene({
id: document.getElementById('map') as HTMLDivElement,
map: new Mapbox({
map: new GaodeMap({
center: [120.19382669582967, 30.258134],
pitch: 0,
style: 'dark',
zoom: 0,
}),
});
// scene.on('loaded', () => {
scene.on('loaded', () => {
const pointLayer = new PointLayer({})
.source(pointsData, {
cluster: false,
})
.scale({
size: {
type: 'power',
field: 'mag',
},
color: {
type: 'linear',
field: 'mag',
},
})
.shape('circle')
// .scale('point_count', {
// type: 'quantile',
// })
.size('mag', [5, 10, 15, 20, 25])
.size('mag', [2, 8, 14, 20, 26, 32, 40])
.animate(false)
.active(true)
.color('yellow')
.color('mag', ['red', 'blue', 'yellow', 'green'])
.style({
opacity: 0.5,
strokeWidth: 1,
});
scene.addLayer(pointLayer);
scene.on('loaded', () => {
const newData = {
type: 'FeatureCollection',
features: pointsData.features.slice(0, 100),
};
pointLayer.setData(newData);
});
this.scene = scene;
const items = pointLayer.getLegendItems('color');
console.log(items);
console.log(pointLayer.getLegendItems('size'));
// scene.on('loaded', () => {
// const newData = {
// type: 'FeatureCollection',
// features: pointsData.features.slice(0, 100),
// };
// pointLayer.setData(newData);
// });
this.scene = scene;
});
}
public render() {