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
package/CHANGELOG.md CHANGED
@@ -1,5 +1,73 @@
1
1
  # Changelog
2
2
 
3
+ ## [3.0.0] - 2026-05-22
4
+
5
+ ### 🚀 BREAKING CHANGE: 总览
6
+
7
+ 3.0 在保持向后兼容的前提下统一了 API 形态、加强了类型约束、把投影管理抽成独立类。所有 2.x 用户都应**配合 [`docs/MIGRATION-3.0.md`](./docs/MIGRATION-3.0.md) 阅读升级**。
8
+
9
+ 升级核心结论:
10
+
11
+ - 真实图层类 `add*` 返回值从原始 OpenLayers layer 改为统一 `LayerHandle`
12
+ - 所有公开 `add*` 方法签名上 `layerName` 现在是**必填**(编译时强制)
13
+ - `addPointByUrl` / `addLineByUrl` / `addPolygonByUrl` 统一异步返回完整 Handle
14
+
15
+ ### ✨ Features
16
+
17
+ - **handle**:新增统一 [`LayerHandle`](./src/types/handle.ts)、`AnimatedLayerHandle` 与 `ControlHandle` 接口;真实图层 add* 返回 `{ layer, remove, setVisible }`,非图层点位返回 `{ target, remove, setVisible }`。
18
+ - **add\***:Point / Line / Polygon 的真实图层 add 方法统一返回 `LayerHandle`;`PulsePointLayerHandle`、`FlowLineLayerHandle` 显式继承动画句柄契约。
19
+ - **\*ByUrl**:`Point.addPointByUrl`、`Line.addLineByUrl`、`Polygon.addPolygonByUrl` 统一先获取 JSON,再调用对应 add 方法并返回 Promise Handle。
20
+ - **Point \*ByUrl**:新增 `Point.addPointByUrl` / `Point.addPulsePointLayerByUrl`,从 URL 直接异步加载点位数据。
21
+ - **ProjectionManager**:投影注册逻辑从 `MyOl` 抽到独立 [`ProjectionManager`](./src/core/projection/ProjectionManager.ts) 类,对外暴露 `register / initialize / resolveViewProjection`。用户现在可在 MyOl 实例之外注册任意 EPSG。
22
+ - **ConfigManager.setDefaults**:运行时修改全局默认配置(如线宽、闪烁颜色),所有未提供该字段的后续调用都生效。配 `resetDefaults` 恢复。
23
+ - **错误体系**:新增 `LayerNotFoundError` / `InvalidGeoJSONError` / `ProjectionError` 三个具体子类,方便调用方 `instanceof` 判别。
24
+ - **类型导出**:补齐 `TwinkleItem` / `VueTemplatePointInstance` / `MeasureHandlerType` 等遗漏类型;运行时枚举 `VueTemplatePointState` 也导出。
25
+
26
+ ### 🐛 Bug Fixes
27
+
28
+ - **destroy 泄漏(P0-1)**:`MyOl.destroy()` 现在会级联调用 `SelectHandler.destroy / Line.destroyAllFlowLines / Point.destroyAll / Polygon.destroyAll`,确保所有 rAF / Overlay / Vue 实例 / Select interaction 释放。
29
+ - **Point/Polygon 句柄注册表**:`Point` / `Polygon` 内部新增 `managedLayers` / `managedDisposables` 注册表,所有 `add*` 都会登记;`destroyAll` 一次性回收。
30
+ - **VueTemplatePoint 实例复用**:`Point.addVueTemplatePoint` 之前每次调用都 `new VueTemplatePoint(map)` 丢弃引用,现在改为单例复用并随 `destroyAll` 一起清理。
31
+ - **Polygon `[key:string]:any` 删除**:`removePolygonLayer` 不再依赖动态属性赋值。
32
+
33
+ ### 🔒 Hardening
34
+
35
+ - 公开 `add*` 方法签名上 `layerName` 变成必填(用 `Options & { layerName: string }` 形式),不动 interface 本身保证 2.x 用户的 options 对象赋值不报错。
36
+ - `MapTools.setMapClip` 的 `baseLayer` 类型从 `any` 收紧为 `BaseLayer`;坐标数组从 `any[]` 收紧为 `number[][]` / `number[][][]`。
37
+ - `PulsePointIconOptions.src` 标 `@deprecated`,统一改名为 `img`(PointPulseLayer 同时识别两者)。
38
+
39
+ ### 📝 Documentation
40
+
41
+ - 新增 [`docs/MIGRATION-3.0.md`](./docs/MIGRATION-3.0.md) 迁移指南。
42
+ - `addPolygonByUrl` / `addLineByUrl` / `Point.addVueTemplatePoint` 等破坏性返回值变化已写入迁移指南。
43
+
44
+ ### 🧪 Tests
45
+
46
+ - 新增 5 个测试文件,共 40 个用例:`destroy-leak`、`config-defaults`、`async-url-api`、`layer-handle`、`config-defaults`,覆盖 P0/P1/P2 所有改动点。
47
+
48
+ ---
49
+
50
+ ## [2.5.5] - Unreleased(P0 补丁版)
51
+
52
+ ### 🐛 Bug Fixes (P0)
53
+
54
+ - **destroy 级联清理**:`MyOl.destroy()` 现在会依次调用 `SelectHandler.destroy / Line.destroyAllFlowLines / Point.destroyAll / Polygon.destroyAll / EventManager.clear`,每一步独立 try/catch,确保所有 RAF、Overlay、Vue app、Select interaction 真正释放。
55
+ - **Point/Polygon 句柄注册表**:`Point.addPoint / addClusterPoint / addPulsePointLayer / addDomPoint / addVueTemplatePoint` 与 `Polygon.addPolygon / addBorderPolygon / addImageLayer / addHeatmap / addMaskLayer / setOutLayer` 创建的图层都注册到内部表,便于 `destroyAll()` 一次性回收。
56
+ - **VueTemplatePoint 实例复用**:`Point.addVueTemplatePoint` 之前每次都 `new VueTemplatePoint(map)` 丢弃引用导致内存泄漏,现改为单例复用。
57
+ - **死代码清理**:删除 `Point.ts` 注释段(旧 `addTwinkleLayerFromPolygon` 实现);删除 `Polygon` 的 `[key:string]:any` 索引签名,重写 `removePolygonLayer`。
58
+
59
+ ### 🔒 Hardening (P0)
60
+
61
+ - `MapTools.setMapClip` 的 `baseLayer` 类型从 `any` 收紧到 `BaseLayer`;坐标数组从 `any[]` 收紧为 `number[][]` / `number[][][]`。
62
+
63
+ ### ✨ Type Exports (P0)
64
+
65
+ - 补齐 `TwinkleItem` / `VueTemplatePointInstance` / `MeasureHandlerType` 等遗漏类型;导出运行时枚举 `VueTemplatePointState`。
66
+
67
+ ### 🧪 Tests (P0)
68
+
69
+ - 新增 `tests/destroy-leak.test.ts`,4 个用例覆盖 `Line.destroyAllFlowLines` / `Point.destroyAll` / `Polygon.destroyAll` / `removePolygonLayer`。
70
+
3
71
  ## [2.5.4] - 2026-05-18
4
72
 
5
73
  ### 🐛 Bug Fixes
package/MyOl.d.ts CHANGED
@@ -24,9 +24,10 @@ export default class MyOl {
24
24
  private readonly configManager;
25
25
  private readonly options;
26
26
  static readonly DefaultOptions: MapInitType;
27
+ /**
28
+ * @deprecated 请使用 ProjectionManager.PROJECTIONS 代替
29
+ */
27
30
  private static readonly PROJECTIONS;
28
- /** *********************内置投影定义*********************/
29
- private static readonly PROJECTION_DEFINITIONS;
30
31
  /**
31
32
  * 构造函数
32
33
  * @param id 地图容器 DOM 元素 ID
@@ -38,21 +39,6 @@ export default class MyOl {
38
39
  * @private
39
40
  */
40
41
  private validateConstructorParams;
41
- /**
42
- * 初始化坐标系
43
- * @private
44
- */
45
- private static initializeProjections;
46
- /**
47
- * 缺失时注册 proj4 投影定义,避免生产构建依赖第三方模块默认副作用。
48
- * @private
49
- */
50
- private static ensureProj4Definition;
51
- /**
52
- * 应用用户显式提供的投影元数据。
53
- * @private
54
- */
55
- private static applyCustomProjectionMetadata;
56
42
  /**
57
43
  * 创建地图控件
58
44
  * @private
@@ -69,11 +55,6 @@ export default class MyOl {
69
55
  * @returns View 地图视图实例
70
56
  */
71
57
  static createView(options?: MapInitType): View;
72
- /**
73
- * 解析视图投影,优先复用已注册投影,避免丢失 proj4 推导的单位信息。
74
- * @private
75
- */
76
- private static resolveViewProjection;
77
58
  /**
78
59
  * 获取视图(向后兼容)
79
60
  * @deprecated 请使用 createView 方法
package/MyOl.js CHANGED
@@ -1,17 +1,16 @@
1
1
  "use strict";
2
2
  // OpenLayers 核心导入
3
- import { register as olProj4Register } from 'ol/proj/proj4';
4
- import { Projection as olProjProjection, addProjection as olProjAddProjection, fromLonLat as olProjFromLonLat, get as olProjGetProjection } from 'ol/proj';
5
3
  import View from "ol/View";
6
4
  import Map from "ol/Map";
7
5
  import { defaults as defaultControls } from 'ol/control';
8
- import proj4 from "proj4";
6
+ import { fromLonLat as olProjFromLonLat } from 'ol/proj';
9
7
  // 内部模块导入
10
8
  import { Polygon } from "./core/polygon";
11
9
  import { Point } from "./core/point";
12
10
  import { Line } from "./core/line";
13
11
  import { MapBaseLayers, MapTools, EventManager, ConfigManager } from "./core/map";
14
12
  import { SelectHandler } from "./core/select";
13
+ import { ProjectionManager } from "./core/projection";
15
14
  import { ErrorHandler, MyOpenLayersError, ErrorType } from './utils/ErrorHandler';
16
15
  /**
17
16
  * MyOl 地图核心类
@@ -37,7 +36,7 @@ class MyOl {
37
36
  // 参数验证
38
37
  this.validateConstructorParams(id, this.options);
39
38
  // 初始化坐标系
40
- MyOl.initializeProjections(this.options);
39
+ ProjectionManager.initialize(this.options);
41
40
  // 准备图层
42
41
  const layers = Array.isArray(this.options.layers) ? this.options.layers : [];
43
42
  // 创建地图实例
@@ -81,75 +80,6 @@ class MyOl {
81
80
  throw new Error(typeof id === 'string' ? `找不到 ID 为 '${id}' 的 DOM 元素` : '提供的 DOM 元素无效');
82
81
  }
83
82
  }
84
- /**
85
- * 初始化坐标系
86
- * @private
87
- */
88
- static initializeProjections(options) {
89
- /** *********************投影定义初始化*********************/
90
- MyOl.ensureProj4Definition(MyOl.PROJECTIONS.WGS84, MyOl.PROJECTION_DEFINITIONS[MyOl.PROJECTIONS.WGS84]);
91
- MyOl.ensureProj4Definition(MyOl.PROJECTIONS.CGCS2000, MyOl.PROJECTION_DEFINITIONS[MyOl.PROJECTIONS.CGCS2000]);
92
- MyOl.ensureProj4Definition(MyOl.PROJECTIONS.CGCS2000_3_DEGREE, MyOl.PROJECTION_DEFINITIONS[MyOl.PROJECTIONS.CGCS2000_3_DEGREE]);
93
- if (options.projection?.code && options.projection.def) {
94
- proj4.defs(options.projection.code, options.projection.def);
95
- }
96
- // 注册到 OpenLayers
97
- olProj4Register(proj4);
98
- // 添加 CGCS2000 投影
99
- const cgsc2000 = new olProjProjection({
100
- code: MyOl.PROJECTIONS.CGCS2000,
101
- extent: [-180, -90, 180, 90],
102
- worldExtent: [-180, -90, 180, 90],
103
- units: "degrees"
104
- });
105
- olProjAddProjection(cgsc2000);
106
- if (options.projection?.code) {
107
- MyOl.applyCustomProjectionMetadata(options.projection);
108
- }
109
- }
110
- /**
111
- * 缺失时注册 proj4 投影定义,避免生产构建依赖第三方模块默认副作用。
112
- * @private
113
- */
114
- static ensureProj4Definition(code, definition) {
115
- if (!proj4.defs(code)) {
116
- proj4.defs(code, definition);
117
- }
118
- }
119
- /**
120
- * 应用用户显式提供的投影元数据。
121
- * @private
122
- */
123
- static applyCustomProjectionMetadata(projection) {
124
- const { code, extent, worldExtent, units } = projection;
125
- const registeredProjection = olProjGetProjection(code);
126
- /** *********************自定义投影元数据初始化*********************/
127
- if (units) {
128
- olProjAddProjection(new olProjProjection({
129
- code,
130
- extent: extent ?? registeredProjection?.getExtent(),
131
- worldExtent: worldExtent ?? registeredProjection?.getWorldExtent(),
132
- units
133
- }));
134
- return;
135
- }
136
- if (registeredProjection) {
137
- if (extent) {
138
- registeredProjection.setExtent(extent);
139
- }
140
- if (worldExtent) {
141
- registeredProjection.setWorldExtent(worldExtent);
142
- }
143
- return;
144
- }
145
- if (extent || worldExtent) {
146
- olProjAddProjection(new olProjProjection({
147
- code,
148
- extent,
149
- worldExtent
150
- }));
151
- }
152
- }
153
83
  /**
154
84
  * 创建地图控件
155
85
  * @private
@@ -184,9 +114,9 @@ class MyOl {
184
114
  static createView(options = MyOl.DefaultOptions) {
185
115
  try {
186
116
  /** *********************静态视图投影初始化*********************/
187
- MyOl.initializeProjections(options);
188
- const code = options.projection?.code ?? MyOl.PROJECTIONS.CGCS2000;
189
- const projection = MyOl.resolveViewProjection(options, code);
117
+ ProjectionManager.initialize(options);
118
+ const code = options.projection?.code ?? ProjectionManager.DEFAULT_PROJECTION;
119
+ const projection = ProjectionManager.resolveViewProjection(options, code);
190
120
  const viewOptions = {
191
121
  projection,
192
122
  center: olProjFromLonLat(options.center, projection),
@@ -201,26 +131,6 @@ class MyOl {
201
131
  throw new MyOpenLayersError(`视图创建失败: ${error instanceof Error ? error.message : '未知错误'}`, ErrorType.MAP_ERROR, { options });
202
132
  }
203
133
  }
204
- /**
205
- * 解析视图投影,优先复用已注册投影,避免丢失 proj4 推导的单位信息。
206
- * @private
207
- */
208
- static resolveViewProjection(options, code) {
209
- const registeredProjection = olProjGetProjection(code);
210
- /** *********************视图投影初始化*********************/
211
- if (registeredProjection
212
- && !options.projection?.extent
213
- && !options.projection?.worldExtent
214
- && !options.projection?.units) {
215
- return registeredProjection;
216
- }
217
- return new olProjProjection({
218
- code,
219
- extent: options.projection?.extent ?? registeredProjection?.getExtent() ?? [-180, -90, 180, 90],
220
- worldExtent: options.projection?.worldExtent ?? registeredProjection?.getWorldExtent() ?? [-180, -90, 180, 90],
221
- units: options.projection?.units ?? registeredProjection?.getUnits() ?? "degrees"
222
- });
223
- }
224
134
  /**
225
135
  * 获取视图(向后兼容)
226
136
  * @deprecated 请使用 createView 方法
@@ -503,16 +413,8 @@ class MyOl {
503
413
  }
504
414
  // 默认配置
505
415
  MyOl.DefaultOptions = ConfigManager.DEFAULT_MYOL_OPTIONS;
506
- // 坐标系配置
507
- MyOl.PROJECTIONS = {
508
- WGS84: "EPSG:4326",
509
- CGCS2000: "EPSG:4490",
510
- CGCS2000_3_DEGREE: "EPSG:4549"
511
- };
512
- /** *********************内置投影定义*********************/
513
- MyOl.PROJECTION_DEFINITIONS = {
514
- [MyOl.PROJECTIONS.WGS84]: "+title=WGS 84 (long/lat) +proj=longlat +ellps=WGS84 +datum=WGS84 +units=degrees",
515
- [MyOl.PROJECTIONS.CGCS2000]: "+proj=longlat +ellps=GRS80 +no_defs",
516
- [MyOl.PROJECTIONS.CGCS2000_3_DEGREE]: "+proj=tmerc +lat_0=0 +lon_0=120 +k=1 +x_0=500000 +y_0=0 +ellps=GRS80 +units=m +no_defs"
517
- };
416
+ /**
417
+ * @deprecated 请使用 ProjectionManager.PROJECTIONS 代替
418
+ */
419
+ MyOl.PROJECTIONS = ProjectionManager.PROJECTIONS;
518
420
  export default MyOl;
package/README.md CHANGED
@@ -6,6 +6,26 @@ my-openlayer 是一个基于 [OpenLayers](https://openlayers.org/) 的现代地
6
6
  [![TypeScript](https://img.shields.io/badge/TypeScript-Ready-blue.svg)](https://www.typescriptlang.org/)
7
7
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
8
8
 
9
+ **[在线 Demo](https://cuteyuchen.github.io/my-openlayer/demo/)** · [文档](https://cuteyuchen.github.io/my-openlayer/) · [迁移指南 (2.x → 3.0)](docs/MIGRATION-3.0.md)
10
+
11
+ ---
12
+
13
+ ## 3.0 亮点
14
+
15
+ 3.0 是一次 API 形态层的范式转移,核心目标是**统一 lifecycle、降低学习成本**:
16
+
17
+ - **统一 LayerHandle** — 真实图层类 `add*` 返回 `{ layer, remove(), setVisible() }` 形态的句柄,跨 Point / Line / Polygon 复用同一套 lifecycle 模型
18
+ - **统一 ControlHandle** — `addDomPoint` / `addVueTemplatePoint` 返回 `{ target, remove(), setVisible() }`,并保留 `anchors` / `getPoints()` 等原有能力
19
+ - ***ByUrl 异步化** — `addPointByUrl` / `addLineByUrl` / `addPolygonByUrl` 统一先获取 JSON,再返回完整 Handle
20
+ - **destroy 级联清理** — `MyOl.destroy()` 现在依次调用 `SelectHandler.destroy` / `Line.destroyAllFlowLines` / `Point.destroyAll` / `Polygon.destroyAll`,确保 rAF / Overlay / Vue 实例 / Select interaction 全部释放
21
+ - **ProjectionManager** — 投影逻辑从 MyOl 内部抽取为独立类,支持 `ProjectionManager.register({ code, def })` 在 MyOl 实例之外注册任意 EPSG
22
+ - **ConfigManager.setDefaults** — 运行时修改全局默认配置,所有未提供该字段的后续调用都生效
23
+ - **具体错误类型** — `LayerNotFoundError` / `InvalidGeoJSONError` / `ProjectionError`,方便 `instanceof` 判别
24
+ - **layerName 必填** — 公开 `add*` 方法签名上 `layerName` 变成必填(编译时强制)
25
+ - **PulsePointIconOptions `src` → `img`** — 统一命名,旧 `src` 标 `@deprecated`
26
+
27
+ > 从 2.x 升级请参考 [MIGRATION-3.0.md](docs/MIGRATION-3.0.md)。3.0 会把旧 `add*` 的原始 layer 返回值改为 Handle;访问 OL 图层请使用 `handle.layer`。
28
+
9
29
  ---
10
30
 
11
31
  ## 项目概述
@@ -32,14 +52,14 @@ my-openlayer 是一个基于 [OpenLayers](https://openlayers.org/) 的现代地
32
52
  - **🛠️ 地图工具**
33
53
  - **测量工具 (MeasureHandler)**:距离和面积测量
34
54
  - **要素选择 (SelectHandler)**:支持点击、悬停、多选等交互选择
35
- - **地图工具 (MapTools)**:图层管理、定位、视图自适应
55
+ - **地图工具 (MapTools)**:图层管理、定位、视图自适应、全图裁剪 (`clipMap`)
36
56
  - **事件管理 (EventManager)**:统一的地图事件监听与管理
37
57
 
38
58
  - **⚡ 高级特性**
39
- - **TypeScript 完全支持**:提供完整的类型定义
40
- - **错误处理 (ErrorHandler)**:统一的错误捕获与日志
41
- - **配置管理 (ConfigManager)**:集中管理默认配置
42
- - **坐标系支持**:内置 `EPSG:4326`、`EPSG:4490`、`EPSG:4549`,支持基于 `proj4.def` 的自定义投影注册
59
+ - **TypeScript 完全支持**:提供完整的类型定义,严格模式
60
+ - **错误处理 (ErrorHandler)**:统一的错误捕获与日志,含具体子类型
61
+ - **配置管理 (ConfigManager)**:集中管理默认配置,支持运行时 `setDefaults`
62
+ - **投影管理 (ProjectionManager)**:内置 `EPSG:4326` / `EPSG:4490` / `EPSG:4549`,支持 `ProjectionManager.register()` 注册任意自定义投影
43
63
 
44
64
  ## 安装
45
65
 
@@ -58,36 +78,52 @@ pnpm add my-openlayer
58
78
  - **proj4**: ^2.7.5
59
79
  - **@turf/turf**: ^7.2.0
60
80
 
81
+ ## CC Switch 技能仓库
82
+
83
+ 本仓库已按 CC Switch 技能仓库结构提供 `my-openlayer-helper`:
84
+
85
+ ```txt
86
+ skills/my-openlayer-helper/SKILL.md
87
+ ```
88
+
89
+ 在 CC Switch 的“添加技能仓库”中填写:
90
+
91
+ ```txt
92
+ 仓库 URL:cuteyuchen/my-openlayer
93
+ 分支:main
94
+ ```
95
+
96
+ CC Switch 扫描后会识别到 `my-openlayer-helper`。后续更新只需要同步本仓库,使用者在 CC Switch 中刷新/更新技能即可。
97
+
61
98
  ## 快速上手
62
99
 
63
100
  ### 1. 初始化地图
64
101
 
65
102
  ```typescript
66
- import MyOl, { MapInitType } from 'my-openlayer';
103
+ import { MyOl } from 'my-openlayer';
67
104
 
68
- const mapConfig: MapInitType = {
105
+ const map = new MyOl('map-container', {
69
106
  center: [119.81, 29.969],
70
107
  zoom: 10,
71
108
  token: import.meta.env.VITE_TIANDITU_TOKEN, // 天地图 Token
72
109
  annotation: true
73
- };
74
-
75
- const map = new MyOl('map-container', mapConfig);
110
+ });
76
111
  ```
77
112
 
78
113
  ### 2. 使用功能模块
79
114
 
80
115
  ```typescript
81
- // 获取模块实例
116
+ // 获取模块实例(懒加载)
82
117
  const point = map.getPoint();
83
118
  const line = map.getLine();
84
119
  const polygon = map.getPolygon();
85
120
 
86
- // 添加点位
87
- point.addPoint([{ lgtd: 119.81, lttd: 29.969, name: '示例点' }], {
88
- layerName: 'example-point',
89
- img: 'marker.png'
90
- });
121
+ // 添加点位(3.0 add* 返回统一 LayerHandle)
122
+ const handle = point.addPoint(
123
+ [{ lgtd: 119.81, lttd: 29.969, name: '示例点' }],
124
+ { layerName: 'example-point', img: 'marker.png' }
125
+ );
126
+ handle?.remove(); // 统一的生命周期管理
91
127
  ```
92
128
 
93
129
  ### 3. 添加高性能闪烁点
@@ -127,20 +163,15 @@ pulseLayer?.remove();
127
163
 
128
164
  ### 4. 添加流动线
129
165
 
130
- `Line` 模块现在支持流光线与流动线图标动画,调用方式与 `Point.addPulsePointLayer()` 保持一致,返回可控 handle。
131
-
132
166
  ```typescript
133
167
  const flow = map.getLine().addFlowLine(lineData, {
134
168
  layerName: 'river-flow',
135
169
  animationMode: 'icon+dash',
136
170
  strokeColor: '#19b1ff',
137
171
  strokeWidth: 3,
138
- lineDash: [18, 12],
139
172
  flowSymbol: {
140
173
  src: '/symbols/boat.svg',
141
174
  scale: 0.9,
142
- color: '#19b1ff',
143
- rotateWithView: true,
144
175
  count: 2,
145
176
  spacing: 0.2
146
177
  }
@@ -151,28 +182,70 @@ flow?.resume();
151
182
  flow?.remove();
152
183
  ```
153
184
 
185
+ ### 5. 全图裁剪
186
+
187
+ ```typescript
188
+ const tools = map.getTools();
189
+
190
+ // 裁剪整张地图(底图 + 注记 + 用户图层全部只在区域内可见)
191
+ tools.clipMap(geoJsonBoundary);
192
+
193
+ // 或只裁剪当前底图
194
+ const baseLayers = map.getMapBaseLayers().getCurrentBaseLayers();
195
+ baseLayers.forEach(layer => MapTools.setMapClip(layer, geoJsonBoundary));
196
+ ```
197
+
198
+ ### 6. 注册自定义投影
199
+
200
+ ```typescript
201
+ import { ProjectionManager } from 'my-openlayer';
202
+
203
+ // 在 MyOl 实例之外注册任意 EPSG
204
+ ProjectionManager.register({
205
+ code: 'EPSG:4528',
206
+ def: '+proj=tmerc +lat_0=0 +lon_0=120 +k=1 +x_0=40500000 +y_0=0 +ellps=GRS80 +units=m +no_defs'
207
+ });
208
+
209
+ // 之后 MyOl 构造时直接用
210
+ const map = new MyOl('map', { projection: { code: 'EPSG:4528' } });
211
+ ```
212
+
213
+ ### 7. 运行时修改默认配置
214
+
215
+ ```typescript
216
+ import { ConfigManager } from 'my-openlayer';
217
+
218
+ // 所有后续 addLine 调用的默认 strokeWidth 变为 4
219
+ ConfigManager.setDefaults('LINE_OPTIONS', { strokeWidth: 4 });
220
+
221
+ // 恢复内置默认
222
+ ConfigManager.resetDefaults('LINE_OPTIONS');
223
+ ```
224
+
154
225
  ## 文档索引
155
226
 
156
- 为了提供更详细的说明,我们将文档拆分为独立的模块文件:
227
+ 详细文档请访问 **[在线文档](https://cuteyuchen.github.io/my-openlayer/)**,交互式 Demo 请访问 **[Demo 站点](https://cuteyuchen.github.io/my-openlayer/demo/)**。
157
228
 
158
229
  ### 核心类库
159
230
  - **[MyOl](docs/MyOl.md)**: 地图入口类,负责初始化和模块访问。
231
+ - **[ProjectionManager](docs/ProjectionManager.md)**: 投影管理(3.0 新增),支持 `register()` 注册任意 EPSG。
160
232
  - **[MapBaseLayers](docs/MapBaseLayers.md)**: 底图与注记管理。
161
- - **[ConfigManager](docs/ConfigManager.md)**: 配置管理。
233
+ - **[ConfigManager](docs/ConfigManager.md)**: 配置管理,支持运行时 `setDefaults`。
162
234
  - **[EventManager](docs/EventManager.md)**: 事件管理。
163
- - **[ErrorHandler](docs/ErrorHandler.md)**: 错误处理。
235
+ - **[ErrorHandler](docs/ErrorHandler.md)**: 错误处理,含 `LayerNotFoundError` / `InvalidGeoJSONError` / `ProjectionError`。
164
236
 
165
237
  ### 要素操作
166
- - **[Point](docs/Point.md)**: 点要素(含聚合、DOM 点位、高性能闪烁点)。
167
- - **[Line](docs/Line.md)**: 线要素。
168
- - **[Polygon](docs/Polygon.md)**: 面要素(含热力图、图片层)。
238
+ - **[Point](docs/Point.md)**: 点要素(含聚合、DOM 点位、高性能闪烁点、统一 Handle)。
239
+ - **[Point](docs/Point.md)**: 点要素(含聚合、DOM 点位、高性能闪烁点、统一 Handle)。
240
+ - **[Line](docs/Line.md)**: 线要素(含静态线、流动线、URL 异步加载)。
241
+ - **[Polygon](docs/Polygon.md)**: 面要素(含热力图、图片层、遮罩层、统一 Handle)。
169
242
  - **[VueTemplatePoint](docs/VueTemplatePoint.md)**: Vue 组件点位。
170
243
  - **[RiverLayerManager](docs/RiverLayerManager.md)**: 河流图层管理。
171
244
 
172
245
  ### 交互与工具
173
246
  - **[SelectHandler](docs/SelectHandler.md)**: 要素选择交互(支持独立样式渲染、多选隔离)。
174
247
  - **[MeasureHandler](docs/MeasureHandler.md)**: 测量工具。
175
- - **[MapTools](docs/MapTools.md)**: 通用地图工具。
248
+ - **[MapTools](docs/MapTools.md)**: 通用地图工具(含 `clipMap` 全图裁剪)。
176
249
  - **[ValidationUtils](docs/ValidationUtils.md)**: 验证工具。
177
250
 
178
251
  ## 详细用法示例
@@ -196,7 +269,21 @@ measure.start('LineString'); // 开始测距
196
269
  measure.end(); // 结束测量
197
270
  ```
198
271
 
199
- > 注意:部分高级功能(如 SelectHandler)可以通过 `map.getSelectHandler()` 访问。
272
+ ### 要素选择
273
+
274
+ ```typescript
275
+ const selectHandler = map.getSelectHandler();
276
+
277
+ selectHandler.enableSelect('click', {
278
+ layerFilter: ['cities'],
279
+ onSelect: (event) => console.log('选中:', event.selected)
280
+ });
281
+
282
+ // 编程式选择
283
+ selectHandler.selectByProperty('name', '杭州', { fitView: true });
284
+ ```
285
+
286
+ > 注意:更多示例请访问 [Demo 站点](https://cuteyuchen.github.io/my-openlayer/demo/),每个公开类都有独立的交互式演示页。
200
287
 
201
288
  ## 贡献指南
202
289
 
@@ -1,7 +1,7 @@
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 type { FlowLineLayerHandle, FlowLineOptions, LineOptions, MapJSONData } from "../../types";
4
+ import type { FlowLineLayerHandle, FlowLineOptions, LineOptions, MapJSONData, LayerHandle } from "../../types";
5
5
  /**
6
6
  * 线要素管理类
7
7
  * 用于在地图上添加和管理静态线与流动线要素。
@@ -23,6 +23,8 @@ export default class Line {
23
23
  * 创建静态线图层。
24
24
  */
25
25
  private createStaticLayer;
26
+ /** *********************统一句柄:静态线图层*********************/
27
+ private toLayerHandle;
26
28
  /**
27
29
  * 注册流动线句柄。
28
30
  */
@@ -31,11 +33,23 @@ export default class Line {
31
33
  * 注销流动线句柄。
32
34
  */
33
35
  private unregisterFlowLineHandle;
34
- addLine(data: MapJSONData, options?: LineOptions): VectorLayer<VectorSource>;
35
- addLineByUrl(url: string, options?: LineOptions): VectorLayer<VectorSource>;
36
+ /** *********************创建静态线图层*********************/
37
+ private createLineLayer;
38
+ /** *********************添加静态线图层*********************/
39
+ addLine(data: MapJSONData, options: LineOptions & {
40
+ layerName: string;
41
+ }): LayerHandle<VectorLayer<VectorSource>>;
42
+ /** *********************从 URL 添加静态线图层*********************/
43
+ addLineByUrl(url: string, options: LineOptions & {
44
+ layerName: string;
45
+ }): Promise<LayerHandle<VectorLayer<VectorSource>>>;
36
46
  removeLineLayer(layerName: string): void;
37
- addFlowLine(data: MapJSONData, options?: FlowLineOptions): FlowLineLayerHandle | null;
38
- addFlowLineByUrl(url: string, options?: FlowLineOptions): Promise<FlowLineLayerHandle | null>;
47
+ addFlowLine(data: MapJSONData, options: FlowLineOptions & {
48
+ layerName: string;
49
+ }): FlowLineLayerHandle | null;
50
+ addFlowLineByUrl(url: string, options: FlowLineOptions & {
51
+ layerName: string;
52
+ }): Promise<FlowLineLayerHandle | null>;
39
53
  removeFlowLineLayer(layerName: string): void;
40
54
  /**
41
55
  * 销毁本实例创建的所有流动线动画。供 MyOl.destroy 调用,
package/core/line/Line.js CHANGED
@@ -61,6 +61,15 @@ export default class Line {
61
61
  this.map.addLayer(layer);
62
62
  return layer;
63
63
  }
64
+ /** *********************统一句柄:静态线图层*********************/
65
+ toLayerHandle(layer) {
66
+ const map = this.map;
67
+ return {
68
+ layer,
69
+ setVisible(visible) { layer.setVisible(visible); },
70
+ remove() { map.removeLayer(layer); }
71
+ };
72
+ }
64
73
  /**
65
74
  * 注册流动线句柄。
66
75
  */
@@ -73,26 +82,32 @@ export default class Line {
73
82
  unregisterFlowLineHandle(layerName) {
74
83
  this.flowLineRegistry.delete(layerName);
75
84
  }
76
- addLine(data, options) {
85
+ /** *********************创建静态线图层*********************/
86
+ createLineLayer(data, options) {
77
87
  ValidationUtils.validateRequired(data, 'GeoJSON data is required');
78
88
  const mergedOptions = this.mergeDefaultOptions(options);
79
89
  const features = new GeoJSON().readFeatures(data, ProjectionUtils.getGeoJSONReadOptions(mergedOptions));
80
90
  return this.createStaticLayer(new VectorSource({ features }), mergedOptions);
81
91
  }
82
- addLineByUrl(url, options = {}) {
92
+ /** *********************添加静态线图层*********************/
93
+ addLine(data, options) {
94
+ return this.toLayerHandle(this.createLineLayer(data, options));
95
+ }
96
+ /** *********************从 URL 添加静态线图层*********************/
97
+ async addLineByUrl(url, options) {
83
98
  ValidationUtils.validateNonEmptyString(url, 'Line url is required');
84
- const mergedOptions = this.mergeDefaultOptions(options);
85
- const source = new VectorSource({
86
- url,
87
- format: new GeoJSON(ProjectionUtils.getGeoJSONReadOptions(mergedOptions))
88
- });
89
- return this.createStaticLayer(source, mergedOptions);
99
+ const response = await fetch(url);
100
+ if (!response.ok) {
101
+ throw new Error(`Failed to fetch line GeoJSON: ${response.status}`);
102
+ }
103
+ const json = await response.json();
104
+ return this.addLine(json, options);
90
105
  }
91
106
  removeLineLayer(layerName) {
92
107
  ValidationUtils.validateLayerName(layerName);
93
108
  MapTools.removeLayer(this.map, layerName);
94
109
  }
95
- addFlowLine(data, options = {}) {
110
+ addFlowLine(data, options) {
96
111
  const mergedOptions = this.mergeFlowLineOptions(options);
97
112
  const layerName = mergedOptions.layerName;
98
113
  try {
@@ -113,7 +128,7 @@ export default class Line {
113
128
  return null;
114
129
  }
115
130
  }
116
- async addFlowLineByUrl(url, options = {}) {
131
+ async addFlowLineByUrl(url, options) {
117
132
  const mergedOptions = this.mergeFlowLineOptions(options);
118
133
  try {
119
134
  ValidationUtils.validateNonEmptyString(url, 'Flow line url is required');