my-openlayer 2.5.5 → 3.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/CHANGELOG.md +68 -0
  2. package/MyOl.d.ts +3 -22
  3. package/MyOl.js +10 -108
  4. package/README.md +116 -29
  5. package/core/line/Line.d.ts +19 -5
  6. package/core/line/Line.js +25 -10
  7. package/core/map/ConfigManager.d.ts +169 -89
  8. package/core/map/ConfigManager.js +157 -175
  9. package/core/map/MapBaseLayers.d.ts +6 -0
  10. package/core/map/MapBaseLayers.js +9 -0
  11. package/core/map/MapTools.d.ts +16 -0
  12. package/core/map/MapTools.js +35 -1
  13. package/core/point/Point.d.ts +40 -14
  14. package/core/point/Point.js +70 -4
  15. package/core/point/PointOverlay.d.ts +2 -4
  16. package/core/point/PointOverlay.js +2 -1
  17. package/core/point/PointPulseLayer.js +6 -4
  18. package/core/polygon/Polygon.d.ts +19 -16
  19. package/core/polygon/Polygon.js +41 -63
  20. package/core/polygon/PolygonHeatmapLayer.js +15 -2
  21. package/core/polygon/PolygonMaskLayer.d.ts +2 -2
  22. package/core/polygon/PolygonMaskLayer.js +3 -1
  23. package/core/polygon/PolygonStyleFactory.d.ts +4 -3
  24. package/core/projection/ProjectionManager.d.ts +66 -0
  25. package/core/projection/ProjectionManager.js +144 -0
  26. package/core/projection/index.d.ts +2 -0
  27. package/core/projection/index.js +1 -0
  28. package/core/select/SelectHandler.d.ts +1 -1
  29. package/core/vue-template-point/VueTemplatePoint.d.ts +2 -4
  30. package/core/vue-template-point/VueTemplatePoint.js +16 -2
  31. package/docs/.vitepress/config.mts +1 -0
  32. package/docs/Line.md +4 -4
  33. package/docs/MIGRATION-3.0.md +221 -0
  34. package/docs/Point.md +24 -6
  35. package/docs/Polygon.md +14 -5
  36. package/index.d.ts +4 -2
  37. package/index.js +2 -1
  38. package/package.json +6 -3
  39. package/types/base.d.ts +4 -4
  40. package/types/common.d.ts +8 -6
  41. package/types/handle.d.ts +34 -0
  42. package/types/handle.js +1 -0
  43. package/types/index.d.ts +1 -0
  44. package/types/line.d.ts +3 -2
  45. package/types/map.d.ts +1 -1
  46. package/types/point.d.ts +11 -3
  47. package/utils/ErrorHandler.d.ts +12 -0
  48. package/utils/ErrorHandler.js +21 -0
@@ -183,6 +183,15 @@ export default class MapBaseLayers {
183
183
  getCurrentBaseLayerType() {
184
184
  return this.currentBaseLayerType;
185
185
  }
186
+ /**
187
+ * 获取当前可见底图对应的 BaseLayer 数组(多 layer 的底图类型会返回多个)。
188
+ * 用于 MapTools.setMapClip 等需要直接操作底图实例的场景。
189
+ */
190
+ getCurrentBaseLayers() {
191
+ if (!this.currentBaseLayerType)
192
+ return [];
193
+ return (this.layers[this.currentBaseLayerType] ?? []);
194
+ }
186
195
  /**
187
196
  * 获取默认底图类型
188
197
  * @private
@@ -34,6 +34,21 @@ export default class MapTools {
34
34
  * 注意:此方法会修改 baseLayer 的 prerender 和 postrender 事件
35
35
  */
36
36
  static setMapClip(baseLayer: BaseLayer, data: MapJSONData): BaseLayer;
37
+ /**
38
+ * 用 GeoJSON 区域裁剪地图上**所有**图层(底图 + 注记 + 用户 vector 层)。
39
+ *
40
+ * 每个图层都会绑定 prerender/postrender 进行 canvas clip。这是 setMapClip
41
+ * 的批量版本,用于"整张地图只在某区域内可见"的需求(例如行政区聚焦)。
42
+ *
43
+ * 实例方法 mapTools.clipMap(data) 会自动用当前 map。
44
+ */
45
+ clipMap(data: MapJSONData): void;
46
+ /**
47
+ * 用 GeoJSON 区域裁剪地图上所有图层(静态版本)。
48
+ * @param map 地图实例
49
+ * @param data 裁剪边界 GeoJSON
50
+ */
51
+ static clipMap(map: Map, data: MapJSONData): void;
37
52
  /**
38
53
  * 移除图层
39
54
  * @param layerName 图层名称
@@ -68,6 +83,7 @@ export default class MapTools {
68
83
  * @param lttd 纬度
69
84
  * @param zoom 缩放级别
70
85
  * @param duration 动画时长
86
+ * @param projection
71
87
  * @returns 定位是否成功
72
88
  */
73
89
  locationAction(lgtd: number, lttd: number, zoom?: number, duration?: number, projection?: {
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  import VectorSource from "ol/source/Vector";
3
3
  import GeoJSON from "ol/format/GeoJSON";
4
+ import ImageStatic from "ol/source/ImageStatic";
4
5
  import { ErrorHandler, ErrorType } from "../../utils/ErrorHandler";
5
6
  import ValidationUtils from "../../utils/ValidationUtils";
6
7
  import ProjectionUtils from "../../utils/ProjectionUtils";
@@ -133,6 +134,38 @@ class MapTools {
133
134
  });
134
135
  return baseLayer;
135
136
  }
137
+ /**
138
+ * 用 GeoJSON 区域裁剪地图上**所有**图层(底图 + 注记 + 用户 vector 层)。
139
+ *
140
+ * 每个图层都会绑定 prerender/postrender 进行 canvas clip。这是 setMapClip
141
+ * 的批量版本,用于"整张地图只在某区域内可见"的需求(例如行政区聚焦)。
142
+ *
143
+ * 实例方法 mapTools.clipMap(data) 会自动用当前 map。
144
+ */
145
+ clipMap(data) {
146
+ if (!this.map) {
147
+ throw new Error('Map instance is not available');
148
+ }
149
+ MapTools.clipMap(this.map, data);
150
+ }
151
+ /**
152
+ * 用 GeoJSON 区域裁剪地图上所有图层(静态版本)。
153
+ * @param map 地图实例
154
+ * @param data 裁剪边界 GeoJSON
155
+ */
156
+ static clipMap(map, data) {
157
+ if (!map) {
158
+ throw new Error('Map instance is required');
159
+ }
160
+ map.getLayers().getArray().forEach(layer => {
161
+ try {
162
+ MapTools.setMapClip(layer, data);
163
+ }
164
+ catch (error) {
165
+ ErrorHandler.getInstance().warn('clipMap: 单层裁剪失败,跳过', { layer, error });
166
+ }
167
+ });
168
+ }
136
169
  /**
137
170
  * 移除图层
138
171
  * @param layerName 图层名称
@@ -183,6 +216,7 @@ class MapTools {
183
216
  * @param lttd 纬度
184
217
  * @param zoom 缩放级别
185
218
  * @param duration 动画时长
219
+ * @param projection
186
220
  * @returns 定位是否成功
187
221
  */
188
222
  locationAction(lgtd, lttd, zoom = 20, duration = 3000, projection) {
@@ -286,7 +320,7 @@ class MapTools {
286
320
  layers.forEach((layer) => {
287
321
  try {
288
322
  const source = layer?.getSource?.();
289
- const extent = source?.getExtent?.();
323
+ const extent = source?.getExtent?.() ?? (source instanceof ImageStatic ? source.getImageExtent() : undefined);
290
324
  if (extent && !isEmpty(extent)) {
291
325
  extend(overallExtent, extent);
292
326
  hasValidExtent = true;
@@ -1,7 +1,8 @@
1
1
  import Map from "ol/Map";
2
2
  import VectorLayer from "ol/layer/Vector";
3
3
  import VectorSource from "ol/source/Vector";
4
- import { PointOptions, ClusterOptions, PointData, VueTemplatePointInstance, TwinkleItem, PulsePointOptions, PulsePointLayerHandle } from '../../types';
4
+ import Overlay from 'ol/Overlay';
5
+ import { PointOptions, ClusterOptions, PointData, VueTemplatePointInstance, TwinkleItem, PulsePointOptions, PulsePointLayerHandle, LayerHandle, ControlHandle } from '../../types';
5
6
  export default class Point {
6
7
  private map;
7
8
  /** 由本实例创建的纯图层(addPoint / addClusterPoint)。destroyAll 时统一移除。 */
@@ -66,40 +67,65 @@ export default class Point {
66
67
  * img: String 图标
67
68
  * }
68
69
  */
69
- addPoint(pointData: PointData[], options: PointOptions): VectorLayer<VectorSource> | null;
70
- addClusterPoint(pointData: PointData[], options: ClusterOptions): VectorLayer<VectorSource> | null;
70
+ /** *********************创建普通点图层*********************/
71
+ private createPointLayer;
72
+ /** *********************创建聚合点图层*********************/
73
+ private createClusterPointLayer;
74
+ /** *********************添加普通点*********************/
75
+ addPoint(pointData: PointData[], options: PointOptions & {
76
+ layerName: string;
77
+ }): LayerHandle<VectorLayer<VectorSource>> | null;
78
+ /** *********************添加聚合点*********************/
79
+ addClusterPoint(pointData: PointData[], options: ClusterOptions & {
80
+ layerName: string;
81
+ }): LayerHandle<VectorLayer<VectorSource>> | null;
82
+ /** @internal 把 VectorLayer 包成 LayerHandle 的内部工具。 */
83
+ private toLayerHandle;
71
84
  /**
72
85
  * 添加高性能闪烁点图层。
73
86
  *
74
87
  * 与 addDomPoint 不同,该方法使用 VectorLayer 批量渲染点位,并通过单个
75
88
  * requestAnimationFrame 驱动闪烁圈,适合村庄预警等大量点位场景。
76
89
  */
77
- addPulsePointLayer(pointData: PointData[], options: PulsePointOptions): PulsePointLayerHandle | null;
90
+ addPulsePointLayer(pointData: PointData[], options: PulsePointOptions & {
91
+ layerName: string;
92
+ }): PulsePointLayerHandle | null;
93
+ /**
94
+ * P1-2:从 URL 加载点位数据并添加为静态点图层。
95
+ *
96
+ * 期望 URL 返回 `PointData[]` 形态的 JSON 数组(含 lgtd / lttd)或 FeatureCollection。
97
+ * features 加载/解析完成后 Promise resolve 为 LayerHandle。
98
+ */
99
+ addPointByUrl(url: string, options: PointOptions & {
100
+ layerName: string;
101
+ }): Promise<LayerHandle<VectorLayer<VectorSource>> | null>;
102
+ /**
103
+ * P1-2:从 URL 加载点位数据并添加为高性能闪烁点图层。
104
+ */
105
+ addPulsePointLayerByUrl(url: string, options: PulsePointOptions & {
106
+ layerName: string;
107
+ }): Promise<PulsePointLayerHandle | null>;
78
108
  /**
79
109
  * 添加闪烁点
80
110
  * @param twinkleList 闪烁点数据
81
111
  * @param callback
82
112
  */
83
- addDomPoint(twinkleList: TwinkleItem[], callback?: Function): {
84
- anchors: import('ol/Overlay').default[];
85
- remove: () => void;
86
- setVisible: (visible: boolean) => void;
113
+ addDomPoint(twinkleList: TwinkleItem[], callback?: Function): ControlHandle<Overlay[]> & {
114
+ anchors: Overlay[];
87
115
  };
88
116
  /**
89
117
  * 添加vue组件为点位
90
118
  * @param pointDataList 点位信息列表
91
119
  * @param template vue组件模板
92
- * @param Vue Vue实例
120
+ * @param options
93
121
  * @returns 返回控制对象,包含显示、隐藏、移除方法
94
122
  * @throws 当参数无效时抛出错误
95
123
  */
96
- addVueTemplatePoint(pointDataList: PointData[], template: any, options?: {
124
+ addVueTemplatePoint(pointDataList: PointData[], template: object, options?: {
97
125
  positioning?: 'bottom-left' | 'bottom-center' | 'bottom-right' | 'center-left' | 'center-center' | 'center-right' | 'top-left' | 'top-center' | 'top-right';
98
126
  stopEvent?: boolean;
99
- }): {
100
- setVisible: (visible: boolean) => void;
101
- setOneVisibleByProp: (propName: string, propValue: any, visible: boolean) => void;
102
- remove: () => void;
127
+ }): ControlHandle<VueTemplatePointInstance[]> & {
128
+ setOneVisibleByProp: (propName: string, propValue: unknown, visible: boolean) => void;
103
129
  getPoints: () => VueTemplatePointInstance[];
104
130
  };
105
131
  /**
@@ -102,7 +102,7 @@ export default class Point {
102
102
  createPointStyle(options, item) {
103
103
  const style = {};
104
104
  if (options.textKey && item) {
105
- style.text = this.createTextStyle(options, item[options.textKey]);
105
+ style.text = this.createTextStyle(options, String(item[options.textKey] ?? ''));
106
106
  }
107
107
  if (options.img) {
108
108
  style.image = this.createIconStyle(options);
@@ -145,7 +145,8 @@ export default class Point {
145
145
  * img: String 图标
146
146
  * }
147
147
  */
148
- addPoint(pointData, options) {
148
+ /** *********************创建普通点图层*********************/
149
+ createPointLayer(pointData, options) {
149
150
  if (!ValidationUtils.validatePointData(pointData)) {
150
151
  return null;
151
152
  }
@@ -187,7 +188,8 @@ export default class Point {
187
188
  this.trackLayer(PointVectorLayer);
188
189
  return PointVectorLayer;
189
190
  }
190
- addClusterPoint(pointData, options) {
191
+ /** *********************创建聚合点图层*********************/
192
+ createClusterPointLayer(pointData, options) {
191
193
  if (!ValidationUtils.validatePointData(pointData)) {
192
194
  return null;
193
195
  }
@@ -197,6 +199,33 @@ export default class Point {
197
199
  }
198
200
  return layer;
199
201
  }
202
+ /** *********************添加普通点*********************/
203
+ addPoint(pointData, options) {
204
+ const layer = this.createPointLayer(pointData, options);
205
+ if (!layer)
206
+ return null;
207
+ return this.toLayerHandle(layer);
208
+ }
209
+ /** *********************添加聚合点*********************/
210
+ addClusterPoint(pointData, options) {
211
+ const layer = this.createClusterPointLayer(pointData, options);
212
+ if (!layer)
213
+ return null;
214
+ return this.toLayerHandle(layer);
215
+ }
216
+ /** @internal 把 VectorLayer 包成 LayerHandle 的内部工具。 */
217
+ toLayerHandle(layer) {
218
+ const map = this.map;
219
+ const managedLayers = this.managedLayers;
220
+ return {
221
+ layer,
222
+ setVisible(visible) { layer.setVisible(visible); },
223
+ remove() {
224
+ managedLayers.delete(layer);
225
+ map.removeLayer(layer);
226
+ }
227
+ };
228
+ }
200
229
  /**
201
230
  * 添加高性能闪烁点图层。
202
231
  *
@@ -211,6 +240,43 @@ export default class Point {
211
240
  this.trackDisposable(handle);
212
241
  return handle;
213
242
  }
243
+ /**
244
+ * P1-2:从 URL 加载点位数据并添加为静态点图层。
245
+ *
246
+ * 期望 URL 返回 `PointData[]` 形态的 JSON 数组(含 lgtd / lttd)或 FeatureCollection。
247
+ * features 加载/解析完成后 Promise resolve 为 LayerHandle。
248
+ */
249
+ async addPointByUrl(url, options) {
250
+ ValidationUtils.validateNonEmptyString(url, 'Point url is required');
251
+ const response = await fetch(url);
252
+ if (!response.ok) {
253
+ throw new Error(`Failed to fetch point data: ${response.status}`);
254
+ }
255
+ const json = await response.json();
256
+ const pointData = Array.isArray(json) ? json : (json?.features ?? []).map((f) => ({
257
+ ...f.properties,
258
+ lgtd: f.geometry?.coordinates?.[0],
259
+ lttd: f.geometry?.coordinates?.[1]
260
+ }));
261
+ return this.addPoint(pointData, options);
262
+ }
263
+ /**
264
+ * P1-2:从 URL 加载点位数据并添加为高性能闪烁点图层。
265
+ */
266
+ async addPulsePointLayerByUrl(url, options) {
267
+ ValidationUtils.validateNonEmptyString(url, 'Pulse point url is required');
268
+ const response = await fetch(url);
269
+ if (!response.ok) {
270
+ throw new Error(`Failed to fetch pulse point data: ${response.status}`);
271
+ }
272
+ const json = await response.json();
273
+ const pointData = Array.isArray(json) ? json : (json?.features ?? []).map((f) => ({
274
+ ...f.properties,
275
+ lgtd: f.geometry?.coordinates?.[0],
276
+ lttd: f.geometry?.coordinates?.[1]
277
+ }));
278
+ return this.addPulsePointLayer(pointData, options);
279
+ }
214
280
  /**
215
281
  * 添加闪烁点
216
282
  * @param twinkleList 闪烁点数据
@@ -225,7 +291,7 @@ export default class Point {
225
291
  * 添加vue组件为点位
226
292
  * @param pointDataList 点位信息列表
227
293
  * @param template vue组件模板
228
- * @param Vue Vue实例
294
+ * @param options
229
295
  * @returns 返回控制对象,包含显示、隐藏、移除方法
230
296
  * @throws 当参数无效时抛出错误
231
297
  */
@@ -1,13 +1,11 @@
1
1
  import Overlay from 'ol/Overlay';
2
2
  import Map from 'ol/Map';
3
- import type { TwinkleItem } from '../../types';
3
+ import type { ControlHandle, TwinkleItem } from '../../types';
4
4
  /**
5
5
  * DOM 点位覆盖物构建器。
6
6
  */
7
7
  export default class PointOverlay {
8
- static create(map: Map, twinkleList: TwinkleItem[], callback?: Function): {
8
+ static create(map: Map, twinkleList: TwinkleItem[], callback?: Function): ControlHandle<Overlay[]> & {
9
9
  anchors: Overlay[];
10
- remove: () => void;
11
- setVisible: (visible: boolean) => void;
12
10
  };
13
11
  }
@@ -36,13 +36,14 @@ export default class PointOverlay {
36
36
  anchors.push(anchor);
37
37
  });
38
38
  return {
39
+ target: anchors,
39
40
  anchors,
40
41
  remove: () => {
41
42
  anchors.forEach(anchor => {
42
43
  anchor.getElement()?.remove();
43
44
  map.removeOverlay(anchor);
44
45
  });
45
- anchors = [];
46
+ anchors.splice(0, anchors.length);
46
47
  },
47
48
  setVisible: (visible) => {
48
49
  anchors.forEach(anchor => {
@@ -84,7 +84,7 @@ export default class PointPulseLayer {
84
84
  });
85
85
  const createStyles = (feature) => {
86
86
  const rawData = feature.get('rawData');
87
- const level = rawData?.[levelKey] ?? 'default';
87
+ const level = String(rawData?.[levelKey] ?? 'default');
88
88
  const progress = frameIndex / Math.max(1, pulseOptions.frameCount - 1);
89
89
  const [minRadius, maxRadius] = pulseOptions.radius;
90
90
  const radius = minRadius + (maxRadius - minRadius) * progress;
@@ -110,9 +110,11 @@ export default class PointPulseLayer {
110
110
  }
111
111
  styles.push(pulseStyle);
112
112
  }
113
- const text = options.textVisible && options.textKey && rawData ? rawData[options.textKey] ?? '' : '';
113
+ const text = options.textVisible && options.textKey && rawData ? String(rawData[options.textKey] ?? '') : '';
114
+ // P1-4: icon.img 是新名,icon.src 保留兼容(@deprecated)
115
+ const iconImg = options.icon?.img ?? options.icon?.src;
114
116
  const staticCacheKey = [
115
- options.img ?? options.icon?.src ?? '',
117
+ options.img ?? iconImg ?? '',
116
118
  options.scale ?? options.icon?.scale ?? ConfigManager.DEFAULT_POINT_ICON_SCALE,
117
119
  options.iconColor ?? options.icon?.color ?? '',
118
120
  text
@@ -123,7 +125,7 @@ export default class PointPulseLayer {
123
125
  return styles;
124
126
  }
125
127
  const pointStyleOptions = {};
126
- const iconSrc = options.img ?? options.icon?.src;
128
+ const iconSrc = options.img ?? iconImg;
127
129
  if (iconSrc) {
128
130
  pointStyleOptions.image = new Icon({
129
131
  src: iconSrc,
@@ -3,7 +3,7 @@ import VectorLayer from "ol/layer/Vector";
3
3
  import VectorSource from "ol/source/Vector";
4
4
  import { Image as ImageLayer } from "ol/layer";
5
5
  import Feature from "ol/Feature";
6
- import { PolygonOptions, MapJSONData, PointData, HeatMapOptions, ImageLayerData, MaskLayerOptions, FeatureColorUpdateOptions } from '../../types';
6
+ import { PolygonOptions, MapJSONData, PointData, HeatMapOptions, ImageLayerData, MaskLayerOptions, FeatureColorUpdateOptions, LayerHandle } from '../../types';
7
7
  /**
8
8
  * Polygon 类用于处理地图上的面要素操作
9
9
  * 包括添加多边形、边框、图片图层、热力图等功能
@@ -24,6 +24,8 @@ export default class Polygon {
24
24
  * @internal
25
25
  */
26
26
  private trackLayer;
27
+ /** *********************统一句柄:OL 图层*********************/
28
+ private toLayerHandle;
27
29
  /**
28
30
  * 销毁本实例创建的所有图层。供 MyOl.destroy 调用。
29
31
  */
@@ -35,7 +37,7 @@ export default class Polygon {
35
37
  * @returns 创建的图层实例
36
38
  * @throws 当数据格式无效时抛出错误
37
39
  */
38
- addBorderPolygon(data: MapJSONData, options?: PolygonOptions): VectorLayer<VectorSource>;
40
+ addBorderPolygon(data: MapJSONData, options?: PolygonOptions): LayerHandle<VectorLayer<VectorSource>>;
39
41
  /**
40
42
  * 从URL添加地图边框图层
41
43
  * @param url 数据URL
@@ -43,7 +45,7 @@ export default class Polygon {
43
45
  * @returns 创建的图层实例
44
46
  * @throws 当数据格式无效时抛出错误
45
47
  */
46
- addBorderPolygonByUrl(url: string, options?: PolygonOptions): VectorLayer<VectorSource>;
48
+ addBorderPolygonByUrl(url: string, options?: PolygonOptions): Promise<LayerHandle<VectorLayer<VectorSource>>>;
47
49
  /**
48
50
  * 添加多边形图层
49
51
  * @param dataJSON GeoJSON 数据
@@ -51,15 +53,16 @@ export default class Polygon {
51
53
  * @returns 创建的矢量图层
52
54
  * @throws 当数据格式无效时抛出错误
53
55
  */
54
- addPolygon(dataJSON: MapJSONData, options?: PolygonOptions): VectorLayer<VectorSource>;
55
- /**
56
- * 从URL添加多边形图层
57
- * @param url 数据URL
58
- * @param options 图层配置选项
59
- * @returns 创建的矢量图层
60
- * @throws 当数据格式无效时抛出错误
61
- */
62
- addPolygonByUrl(url: string, options?: PolygonOptions): VectorLayer<VectorSource>;
56
+ /** *********************创建多边形图层*********************/
57
+ private createPolygonLayer;
58
+ /** *********************添加多边形图层*********************/
59
+ addPolygon(dataJSON: MapJSONData, options: PolygonOptions & {
60
+ layerName: string;
61
+ }): LayerHandle<VectorLayer<VectorSource>>;
62
+ /** *********************从 URL 添加多边形图层*********************/
63
+ addPolygonByUrl(url: string, options: PolygonOptions & {
64
+ layerName: string;
65
+ }): Promise<LayerHandle<VectorLayer<VectorSource>>>;
63
66
  /**
64
67
  * 适应图层视图
65
68
  * @param layer 图层对象
@@ -85,7 +88,7 @@ export default class Polygon {
85
88
  */
86
89
  setOutLayer(data: MapJSONData, options?: {
87
90
  layerName?: string;
88
- extent?: any;
91
+ extent?: number[];
89
92
  fillColor?: string;
90
93
  strokeWidth?: number;
91
94
  strokeColor?: string;
@@ -98,13 +101,13 @@ export default class Polygon {
98
101
  * @returns 创建的图片图层
99
102
  * @throws 当数据格式无效时抛出错误
100
103
  */
101
- addImageLayer(imageData: ImageLayerData, options?: PolygonOptions): ImageLayer<any>;
104
+ addImageLayer(imageData: ImageLayerData, options?: PolygonOptions): LayerHandle<ImageLayer<any>>;
102
105
  /**
103
106
  * 添加热力图图层
104
107
  * @param pointData 点数据数组
105
108
  * @param options 热力图配置
106
109
  */
107
- addHeatmap(pointData: PointData[], options?: HeatMapOptions): import("ol/layer").Heatmap<Feature<import("ol/geom").Geometry>, VectorSource<Feature<import("ol/geom").Geometry>>>;
110
+ addHeatmap(pointData: PointData[], options?: HeatMapOptions): LayerHandle<import("ol/layer").Heatmap<Feature<import("ol/geom").Geometry>, VectorSource<Feature<import("ol/geom").Geometry>>>>;
108
111
  /**
109
112
  * 添加遮罩图层
110
113
  * @param data GeoJSON格式的遮罩数据
@@ -112,6 +115,6 @@ export default class Polygon {
112
115
  * @returns 创建的遮罩图层
113
116
  * @throws 当数据格式无效时抛出错误
114
117
  */
115
- addMaskLayer(data: any, options?: MaskLayerOptions): VectorLayer<VectorSource>;
118
+ addMaskLayer(data: MapJSONData, options?: MaskLayerOptions): LayerHandle<VectorLayer<VectorSource>>;
116
119
  removePolygonLayer(layerName: string): void;
117
120
  }
@@ -2,7 +2,7 @@
2
2
  import VectorLayer from "ol/layer/Vector";
3
3
  import VectorSource from "ol/source/Vector";
4
4
  import GeoJSON from "ol/format/GeoJSON";
5
- import { ErrorHandler } from '../../utils/ErrorHandler';
5
+ import { ErrorHandler, InvalidGeoJSONError, LayerNotFoundError } from '../../utils/ErrorHandler';
6
6
  import ProjectionUtils from '../../utils/ProjectionUtils';
7
7
  import ValidationUtils from '../../utils/ValidationUtils';
8
8
  import { ConfigManager, MapTools } from "../map";
@@ -37,6 +37,19 @@ export default class Polygon {
37
37
  this.managedLayers.add(layer);
38
38
  return layer;
39
39
  }
40
+ /** *********************统一句柄:OL 图层*********************/
41
+ toLayerHandle(layer) {
42
+ const map = this.map;
43
+ const managedLayers = this.managedLayers;
44
+ return {
45
+ layer,
46
+ setVisible(visible) { layer.setVisible(visible); },
47
+ remove() {
48
+ managedLayers.delete(layer);
49
+ map.removeLayer(layer);
50
+ }
51
+ };
52
+ }
40
53
  /**
41
54
  * 销毁本实例创建的所有图层。供 MyOl.destroy 调用。
42
55
  */
@@ -62,13 +75,14 @@ export default class Polygon {
62
75
  ValidationUtils.validateGeoJSONData(data);
63
76
  const mergedOptions = {
64
77
  fillColor: 'rgba(255, 255, 255, 0)',
65
- ...options
78
+ ...options,
79
+ layerName: options?.layerName ?? 'border'
66
80
  };
67
- const layer = this.addPolygon(data, mergedOptions);
81
+ const handle = this.addPolygon(data, mergedOptions);
68
82
  if (mergedOptions.mask) {
69
83
  this.setOutLayer(data);
70
84
  }
71
- return layer;
85
+ return handle;
72
86
  }
73
87
  /**
74
88
  * 从URL添加地图边框图层
@@ -77,14 +91,13 @@ export default class Polygon {
77
91
  * @returns 创建的图层实例
78
92
  * @throws 当数据格式无效时抛出错误
79
93
  */
80
- addBorderPolygonByUrl(url, options) {
94
+ async addBorderPolygonByUrl(url, options) {
81
95
  const mergedOptions = {
82
- layerName: 'border',
83
96
  fillColor: 'rgba(255, 255, 255, 0)',
84
- ...options
97
+ ...options,
98
+ layerName: options?.layerName ?? 'border'
85
99
  };
86
- const layer = this.addPolygonByUrl(url, mergedOptions);
87
- return layer;
100
+ return this.addPolygonByUrl(url, mergedOptions);
88
101
  }
89
102
  /**
90
103
  * 添加多边形图层
@@ -93,7 +106,8 @@ export default class Polygon {
93
106
  * @returns 创建的矢量图层
94
107
  * @throws 当数据格式无效时抛出错误
95
108
  */
96
- addPolygon(dataJSON, options) {
109
+ /** *********************创建多边形图层*********************/
110
+ createPolygonLayer(dataJSON, options) {
97
111
  ValidationUtils.validateGeoJSONData(dataJSON);
98
112
  const mergedOptions = {
99
113
  ...ConfigManager.DEFAULT_POLYGON_OPTIONS,
@@ -118,7 +132,7 @@ export default class Polygon {
118
132
  features = format.readFeatures(dataJSON, ProjectionUtils.getGeoJSONReadOptions(mergedOptions));
119
133
  }
120
134
  catch (error) {
121
- throw new Error(`Failed to parse GeoJSON data: ${error}`);
135
+ throw new InvalidGeoJSONError(error instanceof Error ? error.message : String(error), { layerName: mergedOptions.layerName });
122
136
  }
123
137
  const layer = new VectorLayer({
124
138
  properties: {
@@ -138,55 +152,19 @@ export default class Polygon {
138
152
  }
139
153
  return layer;
140
154
  }
141
- /**
142
- * 从URL添加多边形图层
143
- * @param url 数据URL
144
- * @param options 图层配置选项
145
- * @returns 创建的矢量图层
146
- * @throws 当数据格式无效时抛出错误
147
- */
148
- addPolygonByUrl(url, options) {
149
- const mergedOptions = {
150
- ...ConfigManager.DEFAULT_POLYGON_OPTIONS,
151
- ...options
152
- };
153
- // 如果指定了图层名称,先移除同名图层
154
- if (mergedOptions.layerName) {
155
- new MapTools(this.map).removeLayer(mergedOptions.layerName);
156
- }
157
- const format = new GeoJSON(ProjectionUtils.getGeoJSONReadOptions(mergedOptions));
158
- // 优化:在解析 Feature 时直接注入 layerName,利用解析过程的遍历,避免解析后的二次循环
159
- if (mergedOptions.layerName) {
160
- const originalReadFeatureFromObject = format.readFeatureFromObject;
161
- format.readFeatureFromObject = function (object, options) {
162
- const feature = originalReadFeatureFromObject.call(this, object, options);
163
- feature.set('layerName', mergedOptions.layerName, true); // true 表示静默设置,不触发事件
164
- return feature;
165
- };
166
- }
167
- const source = new VectorSource({
168
- url,
169
- format
170
- });
171
- const layer = new VectorLayer({
172
- properties: {
173
- name: mergedOptions.layerName,
174
- layerName: mergedOptions.layerName
175
- },
176
- source,
177
- style: PolygonStyleFactory.createStyle(mergedOptions),
178
- zIndex: mergedOptions.zIndex
179
- });
180
- layer.setVisible(mergedOptions.visible);
181
- this.map.addLayer(layer);
182
- this.trackLayer(layer);
183
- // 如果需要适应视图
184
- if (mergedOptions.fitView) {
185
- source.once('featuresloadend', () => {
186
- this.fitViewToLayer(layer);
187
- });
155
+ /** *********************添加多边形图层*********************/
156
+ addPolygon(dataJSON, options) {
157
+ return this.toLayerHandle(this.createPolygonLayer(dataJSON, options));
158
+ }
159
+ /** *********************从 URL 添加多边形图层*********************/
160
+ async addPolygonByUrl(url, options) {
161
+ ValidationUtils.validateNonEmptyString(url, 'Polygon url is required');
162
+ const response = await fetch(url);
163
+ if (!response.ok) {
164
+ throw new Error(`Failed to fetch polygon GeoJSON: ${response.status}`);
188
165
  }
189
- return layer;
166
+ const json = await response.json();
167
+ return this.addPolygon(json, options);
190
168
  }
191
169
  /**
192
170
  * 适应图层视图
@@ -209,7 +187,7 @@ export default class Polygon {
209
187
  ValidationUtils.validateLayerName(layerName);
210
188
  const layers = MapTools.getLayerByLayerName(this.map, layerName);
211
189
  if (layers.length === 0) {
212
- throw new Error(`Layer with name '${layerName}' not found`);
190
+ throw new LayerNotFoundError(layerName, { method: 'updateFeatureColor' });
213
191
  }
214
192
  const layer = layers[0];
215
193
  if (!(layer instanceof VectorLayer)) {
@@ -255,7 +233,7 @@ export default class Polygon {
255
233
  addImageLayer(imageData, options) {
256
234
  const layer = PolygonImageLayer.addImageLayer(this.map, imageData, options);
257
235
  this.trackLayer(layer);
258
- return layer;
236
+ return this.toLayerHandle(layer);
259
237
  }
260
238
  /**
261
239
  * 添加热力图图层
@@ -267,7 +245,7 @@ export default class Polygon {
267
245
  if (layer) {
268
246
  this.trackLayer(layer);
269
247
  }
270
- return layer;
248
+ return this.toLayerHandle(layer);
271
249
  }
272
250
  /**
273
251
  * 添加遮罩图层
@@ -279,7 +257,7 @@ export default class Polygon {
279
257
  addMaskLayer(data, options) {
280
258
  const layer = PolygonMaskLayer.addMaskLayer(this.map, data, options);
281
259
  this.trackLayer(layer);
282
- return layer;
260
+ return this.toLayerHandle(layer);
283
261
  }
284
262
  removePolygonLayer(layerName) {
285
263
  new MapTools(this.map).removeLayer(layerName);