• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

antvis / L7Plot / 3730327136

pending completion
3730327136

push

github

yunji
chore: publish

731 of 1962 branches covered (37.26%)

Branch coverage included in aggregate %.

3048 of 4600 relevant lines covered (66.26%)

175.69 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

19.12
/packages/composite-layers/src/composite-layers/icon-layer/icon.ts
1
import { omit, clone, isEqual, isUndefined } from '@antv/util';
2
import { CompositeLayer } from '../../core/composite-layer';
1✔
3
import { TextLayer } from '../../core-layers/text-layer';
1✔
4
import { PointLayer } from '../../core-layers/point-layer';
1✔
5
import { IconLayerOptions, IconLayerActiveOptions, IconLayerSourceOptions } from './types';
1✔
6
import { getDefaultState } from './adaptor';
1✔
7
import { DEFAULT_OPTIONS, DEFAULT_STATE } from './constants';
1✔
8
import { ICoreLayer, Scene, MouseEvent, BlendType, ISource } from '../../types';
1✔
9
import { getLabelLayerOptions } from '../common/label-layer';
1✔
10
import { CompositeLayerEvent } from '../../core/constants';
1✔
11
import { EMPTY_JSON_SOURCE } from '../common/constants';
1✔
12
import { isGestureMultiSelect } from '../common/multi-select';
1✔
13

1✔
14
export abstract class IconLayer<T extends IconLayerOptions> extends CompositeLayer<T> {
1✔
15
  /**
1✔
16
   * 默认配置项
1✔
17
   */
1✔
18
  static DefaultOptions = DEFAULT_OPTIONS;
19
  /**
×
20
   * 图层交互状态配置
21
   */
22
  protected layerState = DEFAULT_STATE;
23
  /**
×
24
   * 复合图层类型
25
   */
26
  public type = CompositeLayer.LayerType.IconImageLayer;
27
  /**
×
28
   * 主图层
29
   */
30
  protected get layer() {
31
    return this.iconLayer;
×
32
  }
33
  /**
34
   * 图标图层
35
   */
×
36
  protected get iconLayer() {
×
37
    return this.subLayers.getLayer('iconLayer') as ICoreLayer;
×
38
  }
39
  /**
×
40
   * 标注文本图层
×
41
   */
42
  public get iconLabelLayer() {
1✔
43
    return this.subLayers.getLayer('labelLayer') as TextLayer;
44
  }
45
  /**
46
   * 选中数据
47
   */
×
48
  private selectData: { feature: any; featureId: number }[] = [];
49
  /**
50
   * 选中填充面图层
51
   */
52
  public get selectIconLayer() {
1✔
53
    return this.subLayers.getLayer('selectIconLayer') as ICoreLayer;
54
  }
55

56
  constructor(options: T) {
57
    super(options);
×
58
    this.initSubLayersEvent();
59
  }
60

61
  /**
62
   * 获取默认配置
1✔
63
   */
64
  public getDefaultOptions(): Partial<IconLayerOptions> {
65
    return IconLayer.DefaultOptions;
66
  }
67

×
68
  /**
69
   * 初始化资源
70
   */
71
  protected abstract initAssets(): void;
72

1✔
73
  /**
74
   * 添加到场景
75
   */
76
  public addTo(scene: Scene) {
77
    this.scene = scene;
×
78
    this.initAssets();
79
    this.subLayers.addTo(scene);
80
    this.emit(CompositeLayerEvent.ADD);
81
  }
82

83
  /**
84
   * 获取图标图层配置项
85
   */
1✔
86
  private getIconLayerOptions() {
×
87
    const {
88
      visible,
89
      minZoom,
90
      maxZoom,
91
      zIndex = 0,
1✔
92
      radius,
×
93
      opacity,
×
94
      icon,
×
95
      fillColor,
×
96
      iconStyle,
97
      ...baseConfig
98
    } = omit<any>(this.options, ['source']) as Omit<T, 'source'>;
99
    const fillState = {
100
      active: this.options.state?.active,
1✔
101
      select: false, // 主图层默认关闭 select
102
    };
×
103
    const fillStyle = {
×
104
      opacity: opacity,
×
105
      ...iconStyle,
106
    };
107
    const options = {
×
108
      ...baseConfig,
×
109
      visible,
×
110
      minZoom,
111
      maxZoom,
1✔
112
      zIndex,
113
      shape: icon,
×
114
      size: radius,
×
115
      state: fillState,
×
116
      color: fillColor,
×
117
      style: fillStyle,
×
118
    };
119

120
    return options;
121
  }
122

123
  private getHighLightLayerOptions() {
124
    const { visible, minZoom, maxZoom, zIndex = 0, icon } = this.options;
125
    const selectState = this.layerState?.select as IconLayerActiveOptions;
126
    const { radius = 5, opacity = 1 } = selectState;
×
127
    const fillStyle = { opacity: opacity };
128

129
    const option = {
×
130
      visible: visible,
131
      blend: BlendType.normal,
1✔
132
      zIndex: zIndex + 0.1,
×
133
      minZoom,
×
134
      maxZoom,
×
135
      source: EMPTY_JSON_SOURCE,
×
136
      size: radius,
×
137
      style: fillStyle,
×
138
      shape: selectState?.icon || icon,
×
139
      state: { select: false, active: false },
140
    };
141

×
142
    return option;
143
  }
×
144

145
  /**
146
   * 图层选中回调
×
147
   */
×
148
  private onSelectHandle = (event: MouseEvent) => {
×
149
    const { feature, featureId } = event;
150
    this.handleSelectData(featureId, feature);
151
  };
×
152

153
  private handleSelectData(featureId: number, feature: any, isSelfMultiSelect?: boolean) {
×
154
    const { enabledMultiSelect, triggerMultiSelectKey } = this.options;
155
    const isMultiSelect = isGestureMultiSelect(enabledMultiSelect, triggerMultiSelectKey) || isSelfMultiSelect;
×
156
    let selectData = clone(this.selectData);
157
    const index = selectData.findIndex((item) => item.featureId === featureId);
1✔
158

×
159
    if (index === -1) {
×
160
      if (isMultiSelect) {
161
        selectData.push({ feature, featureId });
×
162
      } else {
163
        selectData = [{ feature, featureId }];
×
164
      }
165
      this.emit('select', feature, clone(selectData));
×
166
    } else {
×
167
      const unselectFeature = selectData[index];
×
168
      if (isMultiSelect) {
169
        selectData.splice(index, 1);
1✔
170
      } else {
×
171
        selectData = [];
×
172
      }
173
      this.emit('unselect', unselectFeature, clone(selectData));
174
    }
175

176
    this.setSelectLayerSource(selectData);
1✔
177
  }
×
178

×
179
  protected createSubLayers(): ICoreLayer[] {
180
    const source = this.source;
×
181
    this.layerState = getDefaultState(this.options.state);
×
182
    // 映射图标图层
183
    const iconLayer = new PointLayer({
×
184
      ...this.getIconLayerOptions(),
×
185
      id: 'iconLayer',
186
      source,
×
187
      interaction: true,
188
    });
×
189
    // 选中效果
×
190
    const selectLayer = new PointLayer({
×
191
      id: 'selectIconLayer',
192
      ...this.getHighLightLayerOptions(),
×
193
    });
×
194
    // 文本标注
×
195
    const labelLayer = new TextLayer({
×
196
      ...getLabelLayerOptions<IconLayerOptions>(this.options),
197
      id: 'labelLayer',
198
      source,
199
    });
200
    const subLayers = [iconLayer, labelLayer, selectLayer];
1✔
201

×
202
    return subLayers;
×
203
  }
×
204

×
205
  protected updateSubLayers() {
206
    this.iconLayer.update(this.getIconLayerOptions(), false);
207
    this.iconLabelLayer.update(getLabelLayerOptions<IconLayerOptions>(this.options), false);
×
208
  }
×
209

210
  /**
×
211
   * 设置选中描边与填充子图层数据
212
   */
1✔
213
  protected setSelectLayerSource(selectData: any[] = []) {
×
214
    if (
×
215
      this.selectData.length === selectData.length &&
×
216
      isEqual(
×
217
        this.selectData.map(({ featureId }) => featureId),
218
        selectData.map(({ featureId }) => featureId)
219
      )
×
220
    ) {
×
221
      return;
222
    }
223

224
    const features = selectData.map(({ feature }) => feature);
225
    const parser = this.source.parser;
226
    const data = parser.type === 'geojson' ? { type: 'FeatureCollection', features } : features;
1✔
227
    this.selectIconLayer.changeData({ data, parser });
×
228
    this.selectData = selectData;
×
229
  }
230

1✔
231
  /**
×
232
   * 设置子图层数据
×
233
   */
×
234
  protected setSubLayersSource(source: IconLayerSourceOptions | ISource) {
235
    if (this.isSourceInstance(source)) {
236
      this.source = source;
237
      this.iconLayer.setSource(source);
238
      this.iconLabelLayer.setSource(source);
1✔
239
    } else {
×
240
      const { data, ...option } = source;
×
241
      this.source.setData(data, option);
×
242
    }
×
243

×
244
    this.selectIconLayer.changeData(EMPTY_JSON_SOURCE);
245
  }
×
246

247
  protected initSubLayersEvent() {
248
    this.iconLayer.off('click', this.onSelectHandle);
249
    this.selectData = [];
250
    if (!this.options.state) return;
1✔
251
    // active
×
252

×
253
    // select
254
    if (this.options.state.select) {
255
      this.iconLayer.on('click', this.onSelectHandle);
256
    }
257
  }
1✔
258
  /**
259
   * 更新: 更新配置
×
260
   */
×
261
  public updateOption(options: Partial<T>) {
×
262
    super.updateOption(options);
×
263
    this.layerState = getDefaultState(this.options.state);
×
264
  }
265

×
266
  public setIndex(zIndex: number) {
×
267
    this.iconLayer.setIndex(zIndex);
268
    this.iconLabelLayer.setIndex(zIndex);
×
269
    this.selectIconLayer.setIndex(zIndex + 0.1);
×
270
  }
271

272
  /**
273
   * 设置图层高亮状态
274
   */
275
  public setActive(field: string, value: number | string) {
1✔
276
    const source = this.iconLayer.source;
×
277
    const featureId = source.getFeatureId(field, value);
278
    if (isUndefined(featureId)) {
279
      console.warn(`Feature non-existent by field: ${field},value: ${value}`);
280
      return;
281
    }
1✔
282
    this.iconLayer.layer.setActive(featureId);
1✔
283
  }
284

1✔
285
  /**
286
   * 更新
287
   */
288
  public update(options: Partial<T>) {
289
    super.update(options);
290

291
    this.initSubLayersEvent();
292
  }
293

294
  /**
295
   * 设置图层选中状态
296
   */
297
  public setSelect(field: string, value: number | string) {
298
    const source = this.iconLayer.source;
299
    const featureId = source.getFeatureId(field, value);
300
    if (isUndefined(featureId)) {
301
      console.warn(`Feature non-existent by field: ${field},value: ${value}`);
302
      return;
303
    }
304

305
    if (this.layerState?.select && (this.layerState?.select as IconLayerActiveOptions).enable !== false) {
306
      return;
307
    }
308

309
    const feature = source.getFeatureById(featureId);
310
    this.handleSelectData(featureId, feature, true);
311
    // TODO: L7 method pickFeature(id|{x,y})
312
  }
313

314
  /**
315
   * 图层框选数据
316
   */
317
  public boxSelect(bounds: [number, number, number, number], callback: (...args: any[]) => void) {
318
    this.iconLayer.boxSelect(bounds, callback);
319
  }
320
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc