Skip to content

Commit

Permalink
feat: 完善 FlowLayer 箭头、线间隔等新特性 (#301)
Browse files Browse the repository at this point in the history
* feat: 完善 FlowLayer 箭头、线间隔等新特性

* test: 修复 FlowLayer 单测报错问题

* test: 修复 FlowLayer 中线图层异步更新导致的单测失败

* docs: 修复 LineLayer shape 类型中 halfLine => flowline

* refactor: 类型命名更正 StyleAttr => OptionStyleAttribute

---------

Co-authored-by: yanxiong <[email protected]>
  • Loading branch information
heiyexing and heiyexing authored Jul 1, 2023
1 parent 140dd8e commit 6e6dc6f
Show file tree
Hide file tree
Showing 12 changed files with 189 additions and 44 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,8 @@ describe('flow layer', () => {
const dataProvider = new DataProvider();

it('layer', () => {
expect(layer.circleLayer.type).toBe('pointLayer');
expect(layer.lineLayer.type).toBe('lineLayer');
expect(layer.circleLayer?.type).toBe('pointLayer');
expect(layer.lineLayer?.type).toBe('lineLayer');
});

it('data', () => {
Expand All @@ -84,10 +84,9 @@ describe('flow layer', () => {
});

it('style', () => {
expect(layer.circleLayer.options['style'].opacity).toBe(0.5);
expect(layer.circleLayer.options['style'].strokeWidth).toBe(2);
expect(layer.circleLayer.options['style'].stroke).toBe('#f00');

expect(layer.lineLayer.options['style'].opacity).toBe(0.7);
layer.update({});
expect(layer.circleLayer?.options['style'].opacity).toBe(0.5);
expect(layer.circleLayer?.options['style'].strokeWidth).toBe(2);
expect(layer.circleLayer?.options['style'].stroke).toBe('#f00');
});
});
Original file line number Diff line number Diff line change
@@ -1,5 +1,27 @@
import { LineLayerOptions } from '../../core-layers/line-layer/types';
import { PointLayerOptions } from '../../core-layers/point-layer/types';
import { FlowLayerOptions } from './types';

export const EMPTY_CIRCLE_LAYER_SOURCE: PointLayerOptions['source'] = {
data: [],
parser: {
type: 'json',
x: 'lng',
y: 'lat',
},
};

export const EMPTY_LINE_LAYER_SOURCE: LineLayerOptions['source'] = {
data: [],
parser: {
type: 'json',
x: 'fromLng',
y: 'fromLat',
x1: 'toLng',
y1: 'toLat',
},
};

export const DEFAULT_OPTIONS: FlowLayerOptions = {
source: {
data: [],
Expand Down Expand Up @@ -33,6 +55,8 @@ export const DEFAULT_OPTIONS: FlowLayerOptions = {
field: 'weight',
value: [1, 16],
},
lineStroke: '#000',
lineStrokeWidth: 1,
fadeOpacityEnabled: true,
fadeOpacityAmount: 0,
};
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ export function buildIndex(clusterLevels: ClusterLevel[]) {

return {
zoomList,
clusterIdMap,
getAppropriateLevel,
getMapLocations,
getLocationIdsFromCluster,
Expand Down
60 changes: 43 additions & 17 deletions packages/composite-layers/src/composite-layers/flow-layer/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ import { PointLayerOptions } from '../../core-layers/point-layer/types';
import { CompositeLayer } from '../../core/composite-layer';
import { OriginMouseLayerEventList } from '../../core/constants';
import { ICoreLayer, Scene } from '../../types';
import { DEFAULT_OPTIONS } from './constants';
import { DEFAULT_OPTIONS, EMPTY_CIRCLE_LAYER_SOURCE, EMPTY_LINE_LAYER_SOURCE } from './constants';
import { DataProvider } from './data';
import { FlowDataProviderState, FlowLayerOptions, MapStatus } from './types';
import { getColorAttribute, getOpacityColorAttribute, getSizeAttribute } from './utils';
import { getColorAttribute, getLineOffsetsAttribute, getOpacityColorAttribute, getSizeAttribute } from './utils';

export class FlowLayer extends CompositeLayer<FlowLayerOptions> {
/**
Expand All @@ -32,15 +32,15 @@ export class FlowLayer extends CompositeLayer<FlowLayerOptions> {
public dataProviderState!: FlowDataProviderState;

protected get layer() {
return this.lineLayer;
return this.lineLayer!;
}

public get circleLayer() {
return this.subLayers.getLayer('circleLayer')!;
return this.subLayers?.getLayer('circleLayer');
}

public get lineLayer() {
return this.subLayers.getLayer('lineLayer')!;
return this.subLayers?.getLayer('lineLayer');
}

/**
Expand All @@ -52,15 +52,15 @@ export class FlowLayer extends CompositeLayer<FlowLayerOptions> {

protected createSubLayers(): ICoreLayer[] {
const circleLayer = new PointLayer({
...this.getCircleLayerOptions(),
id: 'circleLayer',
name: 'circleLayer',
source: EMPTY_CIRCLE_LAYER_SOURCE,
});

const lineLayer = new LineLayer({
...this.getLineLayerOptions(),
id: 'lineLayer',
name: 'lineLayer',
source: EMPTY_LINE_LAYER_SOURCE,
});

OriginMouseLayerEventList.forEach((eventName) => {
Expand All @@ -87,8 +87,11 @@ export class FlowLayer extends CompositeLayer<FlowLayerOptions> {

protected updateSubLayers() {
this.updateClusterState();
this.circleLayer.update(this.getCircleLayerOptions());
this.lineLayer.update(this.getLineLayerOptions());
this.circleLayer?.update(this.getCircleLayerOptions());
// 保证 lineLayer 获取到的 scale 方法是最新的
requestAnimationFrame(() => {
this.lineLayer?.update(this.getLineLayerOptions());
});
}

protected onMapChange = debounce(
Expand Down Expand Up @@ -165,7 +168,20 @@ export class FlowLayer extends CompositeLayer<FlowLayerOptions> {
}

protected getLineLayerOptions(): LineLayerOptions {
const { minZoom, maxZoom, zIndex, visible, blend, pickingBuffer, lineOpacity: opacity } = this.options;
const {
minZoom,
maxZoom,
zIndex,
visible,
blend,
pickingBuffer,
lineOpacity,
lineWidth,
lineColor,
lineStroke,
lineStrokeOpacity,
lineStrokeWidth,
} = this.options;
const options: LineLayerOptions = {
source: {
data: [],
Expand All @@ -177,17 +193,19 @@ export class FlowLayer extends CompositeLayer<FlowLayerOptions> {
y1: 'toLat',
},
},
shape: 'halfLine',
shape: 'flowline',
minZoom,
maxZoom,
zIndex,
visible,
blend,
pickingBuffer,
style: {
borderColor: '#000',
borderWidth: 1,
opacity,
gapWidth: lineStrokeWidth,
stroke: lineStroke,
strokeWidth: lineStrokeWidth,
strokeOpacity: lineStrokeOpacity,
opacity: lineOpacity,
},
};
if (this.dataProvider && this.scene) {
Expand All @@ -196,13 +214,21 @@ export class FlowLayer extends CompositeLayer<FlowLayerOptions> {
this.options.source,
this.dataProviderState
);

options.source.data = this.dataProvider.getFilterFlows(this.options.source, this.dataProviderState);
options.size = getSizeAttribute(lineWidth!, flowWeightRange);
options.color = getColorAttribute(lineColor!, flowWeightRange);

if (this.options.fadeOpacityEnabled && options.style) {
options.style.opacity = getOpacityColorAttribute(filterFlowWeightRange, this.options.fadeOpacityAmount!);
}
options.source.data = this.dataProvider.getFilterFlows(this.options.source, this.dataProviderState);
options.size = getSizeAttribute(this.options.lineWidth!, flowWeightRange);
options.color = getColorAttribute(this.options.lineColor!, flowWeightRange);

if (this.circleLayer && options.style) {
const clusterIndex = this.dataProvider.getClusterIndex(this.options.source, this.dataProviderState);
options.style.offsets = getLineOffsetsAttribute(clusterIndex, this.circleLayer as PointLayer);
}
}

return options;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -217,9 +217,25 @@ export interface FlowLayerOptions extends CompositeLayerOptions, Partial<Cluster
lineWidth?: LineLayerOptions['size'];

/**
* 客流透明度
* 客流线透明度
*/
lineOpacity?: LineLayerStyleOptions['opacity'];

/**
* 客流线边框颜色
*/
lineStroke?: LineLayerStyleOptions['stroke'];

/**
* 客流线边框宽度
*/
lineStrokeWidth?: LineLayerStyleOptions['strokeWidth'];

/**
* 客流线边框宽度
*/
lineStrokeOpacity?: LineLayerStyleOptions['strokeOpacity'];

/**
* 是否启用根据权重映射半透明值
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { ScaleTypes } from '@antv/l7';
import { Log } from '@antv/scale';
import { LineLayerStyleOptions } from '../../../core-layers/line-layer/types';
import { PointLayer } from '../../../core-layers/point-layer';
import { ColorAttr, SizeAttr } from '../../../types';
import { buildIndex } from '../data/build-index';

const DefaultScaleType = ScaleTypes.LINEAR;

Expand All @@ -16,7 +18,6 @@ export function getSizeAttribute(sizeAttr: SizeAttr, weightRange: [number, numbe
field: 'size',
type: scaleType,
domain: weightRange,
range: value,
},
};
}
Expand All @@ -35,7 +36,6 @@ export function getColorAttribute(colorAttr: ColorAttr, weightRange: [number, nu
field: 'color',
type: scaleType,
domain: weightRange,
range: value,
},
};
}
Expand All @@ -52,10 +52,32 @@ export function getOpacityColorAttribute(
range: [0, 1],
});
const ratio = (1 - fadeOpacityAmount / 100) * 1.5;
return [
'weight',
(weight: any) => {
return {
field: 'weight',
value: (weight: any) => {
return scaleFunc.map(weight) * ratio;
},
];
};
}

export function getLineOffsetsAttribute(
clusterIndex: ReturnType<typeof buildIndex>,
circleLayer: PointLayer
): LineLayerStyleOptions['offsets'] {
const circleLayerSizeAttribute = circleLayer.options.size;
if (typeof circleLayerSizeAttribute === 'number') {
return [circleLayerSizeAttribute, circleLayerSizeAttribute];
} else {
const sizeScale = circleLayer?.layer?.getScale('size');
return {
field: 'fromId*toId',
value: (fromId, toId) => {
const fromCluster = clusterIndex.clusterIdMap.get(fromId);
const toCluster = clusterIndex.clusterIdMap.get(toId);
const fromOffset = fromCluster ? sizeScale(fromCluster.weight) : 0;
const toOffset = toCluster ? sizeScale(toCluster.weight) : 0;
return [fromOffset, toOffset] as [number, number];
},
};
}
}
16 changes: 11 additions & 5 deletions packages/composite-layers/src/core-layers/line-layer/types.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { CoreLayerOptions } from '../../core/core-layer';
import { ShapeAttr } from '../../types';
import { OptionStyleAttribute, ShapeAttr } from '../../types';

/**
* 线图层 图形形状
*/
export type ArcLineShape = 'arc' | 'arc3d' | 'greatcircle';

export type LineShape = 'line' | 'halfLine' | ArcLineShape;
export type LineShape = 'line' | 'flowline' | ArcLineShape;

/**
* 线图层 线类型
Expand All @@ -21,7 +21,7 @@ export enum LineStyleType {
*/
export type LineLayerStyleOptions = {
// 透明度
opacity?: number | [string, (data: any) => number] | [string, [number, number]];
opacity?: OptionStyleAttribute<number>;
// 线类型
lineType?: keyof typeof LineStyleType;
// 虚线间隔
Expand All @@ -43,9 +43,15 @@ export type LineLayerStyleOptions = {
// 纹理混合方式
textureBlend?: string;
// 边框颜色
borderColor?: string;
stroke?: OptionStyleAttribute<string>;
// 边框宽度
borderWidth?: number;
strokeWidth?: OptionStyleAttribute<number>;
// 边框透明度
strokeOpacity?: number;
// 线间隙
gapWidth?: OptionStyleAttribute<number>;
// 线头尾偏移量
offsets?: OptionStyleAttribute<[number, number]>;
};

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { CoreLayerOptions } from '../../core/core-layer';
import { ShapeAttr } from '../../types';
import { OptionStyleAttribute, ShapeAttr } from '../../types';

/**
* 点图层 图层样式
Expand All @@ -8,7 +8,7 @@ export type PointLayerStyleOptions = {
/**
* 透明度
*/
opacity?: number | [string, (data: any) => number] | [string, [number, number]];
opacity?: OptionStyleAttribute<number>;
/**
* 描边宽度
*/
Expand Down Expand Up @@ -62,7 +62,7 @@ export type AnchorType =
*/
export type PointTextLayerStyleOptions = {
/* 透明度 */
opacity?: number | [string, (data: any) => number] | [string, [number, number]];
opacity?: OptionStyleAttribute<number>;
/* 文本相对锚点的位置 */
textAnchor?: AnchorType;
/* 文本相对锚点的偏移量 */
Expand Down
13 changes: 11 additions & 2 deletions packages/composite-layers/src/types/attr.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { IAnimateOption, IActiveOption } from '@antv/l7';
import { ScaleConfig, ISourceCFG } from './common';
import type { IActiveOption, IAnimateOption } from '@antv/l7';
import { ISourceCFG, ScaleConfig } from './common';

export type Callback<T> = (data: Record<string, any>) => T | T[];

Expand Down Expand Up @@ -84,6 +84,15 @@ export type FilterAttr = {
value: Callback<boolean>;
};

/** Style 中的配置项 */
export type OptionStyleAttribute<T> =
| T
| {
field: string;
value: T[] | ((...params: any[]) => T);
}
| [string, T[] | ((...params: any[]) => T)];

/**
* 数据配置
*/
Expand Down
Loading

0 comments on commit 6e6dc6f

Please sign in to comment.