my-openlayer 2.5.4 → 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 +52 -112
  4. package/README.md +116 -29
  5. package/core/line/Line.d.ts +24 -5
  6. package/core/line/Line.js +38 -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 +17 -1
  12. package/core/map/MapTools.js +39 -5
  13. package/core/point/Point.d.ts +54 -14
  14. package/core/point/Point.js +132 -76
  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 +32 -17
  19. package/core/polygon/Polygon.js +87 -64
  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 +6 -3
  37. package/index.js +4 -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
@@ -1,4 +1,3 @@
1
- "use strict";
2
1
  import Feature from "ol/Feature";
3
2
  import { Point as olPoint } from "ol/geom";
4
3
  import { Text, Style, Fill, Stroke, Icon } from "ol/style";
@@ -13,8 +12,48 @@ import PointOverlay from './PointOverlay';
13
12
  import PointPulseLayer from './PointPulseLayer';
14
13
  export default class Point {
15
14
  constructor(map) {
15
+ /** 由本实例创建的纯图层(addPoint / addClusterPoint)。destroyAll 时统一移除。 */
16
+ this.managedLayers = new Set();
17
+ /** 由本实例创建的、需要主动 remove 的句柄(Pulse / DOM / Vue 模板)。 */
18
+ this.managedDisposables = new Set();
16
19
  this.map = map;
17
20
  }
21
+ /** @internal */
22
+ trackLayer(layer) {
23
+ this.managedLayers.add(layer);
24
+ return layer;
25
+ }
26
+ /** @internal */
27
+ trackDisposable(handle) {
28
+ this.managedDisposables.add(handle);
29
+ return handle;
30
+ }
31
+ /**
32
+ * 销毁本实例创建的所有图层与动画句柄。供 MyOl.destroy 调用。
33
+ */
34
+ destroyAll() {
35
+ this.managedDisposables.forEach(handle => {
36
+ try {
37
+ handle.remove();
38
+ }
39
+ catch { /* ignore */ }
40
+ });
41
+ this.managedDisposables.clear();
42
+ this.managedLayers.forEach(layer => {
43
+ try {
44
+ this.map.removeLayer(layer);
45
+ }
46
+ catch { /* ignore */ }
47
+ });
48
+ this.managedLayers.clear();
49
+ if (this.vueTemplatePoint) {
50
+ try {
51
+ this.vueTemplatePoint.removeAllPoints();
52
+ }
53
+ catch { /* ignore */ }
54
+ this.vueTemplatePoint = undefined;
55
+ }
56
+ }
18
57
  /**
19
58
  * 创建文本样式
20
59
  * @private
@@ -63,7 +102,7 @@ export default class Point {
63
102
  createPointStyle(options, item) {
64
103
  const style = {};
65
104
  if (options.textKey && item) {
66
- style.text = this.createTextStyle(options, item[options.textKey]);
105
+ style.text = this.createTextStyle(options, String(item[options.textKey] ?? ''));
67
106
  }
68
107
  if (options.img) {
69
108
  style.image = this.createIconStyle(options);
@@ -106,7 +145,8 @@ export default class Point {
106
145
  * img: String 图标
107
146
  * }
108
147
  */
109
- addPoint(pointData, options) {
148
+ /** *********************创建普通点图层*********************/
149
+ createPointLayer(pointData, options) {
110
150
  if (!ValidationUtils.validatePointData(pointData)) {
111
151
  return null;
112
152
  }
@@ -135,20 +175,56 @@ export default class Point {
135
175
  pointFeatureList.push(pointFeature);
136
176
  });
137
177
  const PointVectorLayer = new VectorLayer({
138
- layerName: options.layerName,
178
+ properties: {
179
+ name: options.layerName,
180
+ layerName: options.layerName
181
+ },
139
182
  source: new VectorSource({
140
183
  features: pointFeatureList
141
184
  }),
142
185
  zIndex: options.zIndex || ConfigManager.DEFAULT_POINT_OPTIONS.zIndex,
143
186
  });
144
187
  this.configureLayer(PointVectorLayer, options);
188
+ this.trackLayer(PointVectorLayer);
145
189
  return PointVectorLayer;
146
190
  }
147
- addClusterPoint(pointData, options) {
191
+ /** *********************创建聚合点图层*********************/
192
+ createClusterPointLayer(pointData, options) {
148
193
  if (!ValidationUtils.validatePointData(pointData)) {
149
194
  return null;
150
195
  }
151
- return PointClusterLayer.create(this.map, pointData, options, this.createClusterStyle.bind(this));
196
+ const layer = PointClusterLayer.create(this.map, pointData, options, this.createClusterStyle.bind(this));
197
+ if (layer) {
198
+ this.trackLayer(layer);
199
+ }
200
+ return layer;
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
+ };
152
228
  }
153
229
  /**
154
230
  * 添加高性能闪烁点图层。
@@ -160,86 +236,62 @@ export default class Point {
160
236
  if (!ValidationUtils.validatePointData(pointData)) {
161
237
  return null;
162
238
  }
163
- return PointPulseLayer.create(this.map, pointData, options);
239
+ const handle = PointPulseLayer.create(this.map, pointData, options);
240
+ this.trackDisposable(handle);
241
+ return handle;
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);
164
279
  }
165
- // // 在流域中心添加闪烁点位
166
- // addTwinkleLayerFromPolygon(twinkleList: any[], className: string, key: string, json: MapJSONData, options?: PolygonOptions) {
167
- // new MapTools(this.map).removeLayer('twinklePoint')
168
- // // 计算多边形的中心点坐标
169
- // const calculatePolygonCenter = (polygonCoordinates: any) => {
170
- // const polygon = turf.polygon(polygonCoordinates[0]);
171
- // const centroid = turf.centroid(polygon);
172
- // return centroid.geometry.coordinates;
173
- // };
174
- // const features: any[] = json.features
175
- // const vectorSource = new VectorSource({
176
- // format: new GeoJSON(),
177
- // });
178
- // twinkleList.forEach(item => {
179
- // const feature = features.find((ele: any) => {
180
- // return ele.properties.BASIN === item.idx
181
- // })
182
- // if (!feature) return
183
- // feature.properties.level = item.lev
184
- // const geojson = new GeoJSON();
185
- // const olFeature = geojson.readFeature(feature);
186
- // if (Array.isArray(olFeature)) {
187
- // vectorSource.addFeatures(olFeature);
188
- // } else {
189
- // vectorSource.addFeature(olFeature);
190
- // }
191
- // if (feature) {
192
- // const polygonCenter = calculatePolygonCenter(feature.geometry.coordinates)
193
- // item.lgtd = polygonCenter[0]
194
- // item.lttd = polygonCenter[1]
195
- // }
196
- // })
197
- // const basinLayer = new VectorLayer({
198
- // name: 'twinklePoint',
199
- // layerName: 'twinklePoint',
200
- // source: vectorSource,
201
- // style: function (feature: any) {
202
- // if (options?.style) {
203
- // if (typeof options.style === 'function') {
204
- // return options.style(feature);
205
- // } else {
206
- // return options.style;
207
- // }
208
- // }
209
- // return new Style({
210
- // stroke: new Stroke({
211
- // color: 'rgb(139,188,245)',
212
- // width: 3
213
- // }),
214
- // fill: new Fill({ color: 'rgba(255, 255, 255, 0)' }),
215
- // text: new Text({
216
- // text: feature.values_['BASIN'] || "",
217
- // font: '14px Calibri,sans-serif',
218
- // fill: new Fill({ color: '#FFF' }),
219
- // stroke: new Stroke({
220
- // color: '#409EFF', width: 2
221
- // }),
222
- // })
223
- // })
224
- // },
225
- // zIndex: 21
226
- // } as any)
227
- // this.map.addLayer(basinLayer)
228
- // this.addTwinkleLayer(twinkleList.map(item => ({...item, className: item[key]})), className, key)
229
- // }
230
280
  /**
231
281
  * 添加闪烁点
232
282
  * @param twinkleList 闪烁点数据
233
283
  * @param callback
234
284
  */
235
285
  addDomPoint(twinkleList, callback) {
236
- return PointOverlay.create(this.map, twinkleList, callback);
286
+ const handle = PointOverlay.create(this.map, twinkleList, callback);
287
+ this.trackDisposable(handle);
288
+ return handle;
237
289
  }
238
290
  /**
239
291
  * 添加vue组件为点位
240
292
  * @param pointDataList 点位信息列表
241
293
  * @param template vue组件模板
242
- * @param Vue Vue实例
294
+ * @param options
243
295
  * @returns 返回控制对象,包含显示、隐藏、移除方法
244
296
  * @throws 当参数无效时抛出错误
245
297
  */
@@ -251,8 +303,12 @@ export default class Point {
251
303
  throw new Error('Vue template is required');
252
304
  }
253
305
  try {
254
- const vueTemplatePoint = new VueTemplatePoint(this.map);
255
- return vueTemplatePoint.addVueTemplatePoint(pointDataList, template, options);
306
+ if (!this.vueTemplatePoint) {
307
+ this.vueTemplatePoint = new VueTemplatePoint(this.map);
308
+ }
309
+ const handle = this.vueTemplatePoint.addVueTemplatePoint(pointDataList, template, options);
310
+ this.trackDisposable(handle);
311
+ return handle;
256
312
  }
257
313
  catch (error) {
258
314
  throw new Error(`Failed to create Vue template points: ${error}`);
@@ -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,19 +3,33 @@ 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
  * 包括添加多边形、边框、图片图层、热力图等功能
10
10
  */
11
11
  export default class Polygon {
12
12
  private map;
13
- [key: string]: any;
13
+ /**
14
+ * 由本实例创建的所有图层句柄(含 mask 子图层等)。destroyAll 时统一回收,避免地图销毁后图层泄漏。
15
+ */
16
+ private readonly managedLayers;
14
17
  /**
15
18
  * 构造函数
16
19
  * @param map OpenLayers 地图实例
17
20
  */
18
21
  constructor(map: Map);
22
+ /**
23
+ * 跟踪一个本实例创建的图层,便于销毁阶段统一清理。
24
+ * @internal
25
+ */
26
+ private trackLayer;
27
+ /** *********************统一句柄:OL 图层*********************/
28
+ private toLayerHandle;
29
+ /**
30
+ * 销毁本实例创建的所有图层。供 MyOl.destroy 调用。
31
+ */
32
+ destroyAll(): void;
19
33
  /**
20
34
  * 添加地图边框图层
21
35
  * @param data 图层数据,必须是有效的 GeoJSON 格式
@@ -23,7 +37,7 @@ export default class Polygon {
23
37
  * @returns 创建的图层实例
24
38
  * @throws 当数据格式无效时抛出错误
25
39
  */
26
- addBorderPolygon(data: MapJSONData, options?: PolygonOptions): VectorLayer<VectorSource>;
40
+ addBorderPolygon(data: MapJSONData, options?: PolygonOptions): LayerHandle<VectorLayer<VectorSource>>;
27
41
  /**
28
42
  * 从URL添加地图边框图层
29
43
  * @param url 数据URL
@@ -31,7 +45,7 @@ export default class Polygon {
31
45
  * @returns 创建的图层实例
32
46
  * @throws 当数据格式无效时抛出错误
33
47
  */
34
- addBorderPolygonByUrl(url: string, options?: PolygonOptions): VectorLayer<VectorSource>;
48
+ addBorderPolygonByUrl(url: string, options?: PolygonOptions): Promise<LayerHandle<VectorLayer<VectorSource>>>;
35
49
  /**
36
50
  * 添加多边形图层
37
51
  * @param dataJSON GeoJSON 数据
@@ -39,15 +53,16 @@ export default class Polygon {
39
53
  * @returns 创建的矢量图层
40
54
  * @throws 当数据格式无效时抛出错误
41
55
  */
42
- addPolygon(dataJSON: MapJSONData, options?: PolygonOptions): VectorLayer<VectorSource>;
43
- /**
44
- * 从URL添加多边形图层
45
- * @param url 数据URL
46
- * @param options 图层配置选项
47
- * @returns 创建的矢量图层
48
- * @throws 当数据格式无效时抛出错误
49
- */
50
- 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>>>;
51
66
  /**
52
67
  * 适应图层视图
53
68
  * @param layer 图层对象
@@ -73,7 +88,7 @@ export default class Polygon {
73
88
  */
74
89
  setOutLayer(data: MapJSONData, options?: {
75
90
  layerName?: string;
76
- extent?: any;
91
+ extent?: number[];
77
92
  fillColor?: string;
78
93
  strokeWidth?: number;
79
94
  strokeColor?: string;
@@ -86,13 +101,13 @@ export default class Polygon {
86
101
  * @returns 创建的图片图层
87
102
  * @throws 当数据格式无效时抛出错误
88
103
  */
89
- addImageLayer(imageData: ImageLayerData, options?: PolygonOptions): ImageLayer<any>;
104
+ addImageLayer(imageData: ImageLayerData, options?: PolygonOptions): LayerHandle<ImageLayer<any>>;
90
105
  /**
91
106
  * 添加热力图图层
92
107
  * @param pointData 点数据数组
93
108
  * @param options 热力图配置
94
109
  */
95
- 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>>>>;
96
111
  /**
97
112
  * 添加遮罩图层
98
113
  * @param data GeoJSON格式的遮罩数据
@@ -100,6 +115,6 @@ export default class Polygon {
100
115
  * @returns 创建的遮罩图层
101
116
  * @throws 当数据格式无效时抛出错误
102
117
  */
103
- addMaskLayer(data: any, options?: MaskLayerOptions): VectorLayer<VectorSource>;
118
+ addMaskLayer(data: MapJSONData, options?: MaskLayerOptions): LayerHandle<VectorLayer<VectorSource>>;
104
119
  removePolygonLayer(layerName: string): void;
105
120
  }