my-openlayer 2.5.5 → 3.0.2
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.
- package/CHANGELOG.md +68 -0
- package/MyOl.d.ts +3 -22
- package/MyOl.js +10 -108
- package/README.md +116 -31
- package/core/line/Line.d.ts +19 -5
- package/core/line/Line.js +25 -10
- package/core/line/index.d.ts +0 -2
- package/core/line/index.js +0 -1
- package/core/map/ConfigManager.d.ts +137 -99
- package/core/map/ConfigManager.js +145 -175
- package/core/map/MapBaseLayers.d.ts +6 -0
- package/core/map/MapBaseLayers.js +9 -0
- package/core/map/MapTools.d.ts +16 -0
- package/core/map/MapTools.js +35 -1
- package/core/point/Point.d.ts +40 -14
- package/core/point/Point.js +70 -4
- package/core/point/PointOverlay.d.ts +2 -4
- package/core/point/PointOverlay.js +2 -1
- package/core/point/PointPulseLayer.js +6 -4
- package/core/polygon/Polygon.d.ts +19 -16
- package/core/polygon/Polygon.js +41 -63
- package/core/polygon/PolygonHeatmapLayer.js +15 -2
- package/core/polygon/PolygonMaskLayer.d.ts +2 -2
- package/core/polygon/PolygonMaskLayer.js +3 -1
- package/core/polygon/PolygonStyleFactory.d.ts +4 -3
- package/core/projection/ProjectionManager.d.ts +66 -0
- package/core/projection/ProjectionManager.js +144 -0
- package/core/projection/index.d.ts +2 -0
- package/core/projection/index.js +1 -0
- package/core/select/SelectHandler.d.ts +1 -1
- package/core/vue-template-point/VueTemplatePoint.d.ts +2 -4
- package/core/vue-template-point/VueTemplatePoint.js +16 -2
- package/docs/.vitepress/config.mts +2 -2
- package/docs/Line.md +4 -4
- package/docs/MIGRATION-3.0.md +221 -0
- package/docs/Point.md +24 -6
- package/docs/Polygon.md +14 -5
- package/index.d.ts +5 -4
- package/index.js +3 -2
- package/package.json +6 -3
- package/types/base.d.ts +4 -4
- package/types/common.d.ts +8 -6
- package/types/handle.d.ts +34 -0
- package/types/handle.js +1 -0
- package/types/index.d.ts +1 -0
- package/types/line.d.ts +3 -2
- package/types/map.d.ts +1 -1
- package/types/point.d.ts +11 -3
- package/utils/ErrorHandler.d.ts +12 -0
- package/utils/ErrorHandler.js +21 -0
- package/core/line/RiverLayerManager.d.ts +0 -93
- package/core/line/RiverLayerManager.js +0 -340
- package/docs/RiverLayerManager.md +0 -187
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
|
|
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
|
-
|
|
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
|
-
|
|
188
|
-
const code = options.projection?.code ??
|
|
189
|
-
const projection =
|
|
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
|
-
|
|
508
|
-
|
|
509
|
-
|
|
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
|
[](https://www.typescriptlang.org/)
|
|
7
7
|
[](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
|
## 项目概述
|
|
@@ -27,19 +47,18 @@ my-openlayer 是一个基于 [OpenLayers](https://openlayers.org/) 的现代地
|
|
|
27
47
|
- **线要素绘制 (Line)**:支持静态线、流光线、流动线图标动画,详见 [Line 动画文档](docs/Line.md#流动线--动态图标线)
|
|
28
48
|
- **面要素 (Polygon)**:面要素绘制、分区高亮、遮罩层
|
|
29
49
|
- **Vue组件支持 (VueTemplatePoint)**:支持将 Vue 组件作为地图点位渲染
|
|
30
|
-
- **河流图层 (RiverLayerManager)**:支持分级显示的河流图层管理
|
|
31
50
|
|
|
32
51
|
- **🛠️ 地图工具**
|
|
33
52
|
- **测量工具 (MeasureHandler)**:距离和面积测量
|
|
34
53
|
- **要素选择 (SelectHandler)**:支持点击、悬停、多选等交互选择
|
|
35
|
-
- **地图工具 (MapTools)
|
|
54
|
+
- **地图工具 (MapTools)**:图层管理、定位、视图自适应、全图裁剪 (`clipMap`)
|
|
36
55
|
- **事件管理 (EventManager)**:统一的地图事件监听与管理
|
|
37
56
|
|
|
38
57
|
- **⚡ 高级特性**
|
|
39
|
-
- **TypeScript
|
|
40
|
-
- **错误处理 (ErrorHandler)
|
|
41
|
-
- **配置管理 (ConfigManager)
|
|
42
|
-
-
|
|
58
|
+
- **TypeScript 完全支持**:提供完整的类型定义,严格模式
|
|
59
|
+
- **错误处理 (ErrorHandler)**:统一的错误捕获与日志,含具体子类型
|
|
60
|
+
- **配置管理 (ConfigManager)**:集中管理默认配置,支持运行时 `setDefaults`
|
|
61
|
+
- **投影管理 (ProjectionManager)**:内置 `EPSG:4326` / `EPSG:4490` / `EPSG:4549`,支持 `ProjectionManager.register()` 注册任意自定义投影
|
|
43
62
|
|
|
44
63
|
## 安装
|
|
45
64
|
|
|
@@ -58,36 +77,52 @@ pnpm add my-openlayer
|
|
|
58
77
|
- **proj4**: ^2.7.5
|
|
59
78
|
- **@turf/turf**: ^7.2.0
|
|
60
79
|
|
|
80
|
+
## CC Switch 技能仓库
|
|
81
|
+
|
|
82
|
+
本仓库已按 CC Switch 技能仓库结构提供 `my-openlayer-helper`:
|
|
83
|
+
|
|
84
|
+
```txt
|
|
85
|
+
skills/my-openlayer-helper/SKILL.md
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
在 CC Switch 的“添加技能仓库”中填写:
|
|
89
|
+
|
|
90
|
+
```txt
|
|
91
|
+
仓库 URL:cuteyuchen/my-openlayer
|
|
92
|
+
分支:main
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
CC Switch 扫描后会识别到 `my-openlayer-helper`。后续更新只需要同步本仓库,使用者在 CC Switch 中刷新/更新技能即可。
|
|
96
|
+
|
|
61
97
|
## 快速上手
|
|
62
98
|
|
|
63
99
|
### 1. 初始化地图
|
|
64
100
|
|
|
65
101
|
```typescript
|
|
66
|
-
import
|
|
102
|
+
import { MyOl } from 'my-openlayer';
|
|
67
103
|
|
|
68
|
-
const
|
|
104
|
+
const map = new MyOl('map-container', {
|
|
69
105
|
center: [119.81, 29.969],
|
|
70
106
|
zoom: 10,
|
|
71
107
|
token: import.meta.env.VITE_TIANDITU_TOKEN, // 天地图 Token
|
|
72
108
|
annotation: true
|
|
73
|
-
};
|
|
74
|
-
|
|
75
|
-
const map = new MyOl('map-container', mapConfig);
|
|
109
|
+
});
|
|
76
110
|
```
|
|
77
111
|
|
|
78
112
|
### 2. 使用功能模块
|
|
79
113
|
|
|
80
114
|
```typescript
|
|
81
|
-
//
|
|
115
|
+
// 获取模块实例(懒加载)
|
|
82
116
|
const point = map.getPoint();
|
|
83
117
|
const line = map.getLine();
|
|
84
118
|
const polygon = map.getPolygon();
|
|
85
119
|
|
|
86
|
-
//
|
|
87
|
-
point.addPoint(
|
|
88
|
-
|
|
89
|
-
img: 'marker.png'
|
|
90
|
-
|
|
120
|
+
// 添加点位(3.0 add* 返回统一 LayerHandle)
|
|
121
|
+
const handle = point.addPoint(
|
|
122
|
+
[{ lgtd: 119.81, lttd: 29.969, name: '示例点' }],
|
|
123
|
+
{ layerName: 'example-point', img: 'marker.png' }
|
|
124
|
+
);
|
|
125
|
+
handle?.remove(); // 统一的生命周期管理
|
|
91
126
|
```
|
|
92
127
|
|
|
93
128
|
### 3. 添加高性能闪烁点
|
|
@@ -127,20 +162,15 @@ pulseLayer?.remove();
|
|
|
127
162
|
|
|
128
163
|
### 4. 添加流动线
|
|
129
164
|
|
|
130
|
-
`Line` 模块现在支持流光线与流动线图标动画,调用方式与 `Point.addPulsePointLayer()` 保持一致,返回可控 handle。
|
|
131
|
-
|
|
132
165
|
```typescript
|
|
133
166
|
const flow = map.getLine().addFlowLine(lineData, {
|
|
134
167
|
layerName: 'river-flow',
|
|
135
168
|
animationMode: 'icon+dash',
|
|
136
169
|
strokeColor: '#19b1ff',
|
|
137
170
|
strokeWidth: 3,
|
|
138
|
-
lineDash: [18, 12],
|
|
139
171
|
flowSymbol: {
|
|
140
172
|
src: '/symbols/boat.svg',
|
|
141
173
|
scale: 0.9,
|
|
142
|
-
color: '#19b1ff',
|
|
143
|
-
rotateWithView: true,
|
|
144
174
|
count: 2,
|
|
145
175
|
spacing: 0.2
|
|
146
176
|
}
|
|
@@ -151,28 +181,69 @@ flow?.resume();
|
|
|
151
181
|
flow?.remove();
|
|
152
182
|
```
|
|
153
183
|
|
|
184
|
+
### 5. 全图裁剪
|
|
185
|
+
|
|
186
|
+
```typescript
|
|
187
|
+
const tools = map.getTools();
|
|
188
|
+
|
|
189
|
+
// 裁剪整张地图(底图 + 注记 + 用户图层全部只在区域内可见)
|
|
190
|
+
tools.clipMap(geoJsonBoundary);
|
|
191
|
+
|
|
192
|
+
// 或只裁剪当前底图
|
|
193
|
+
const baseLayers = map.getMapBaseLayers().getCurrentBaseLayers();
|
|
194
|
+
baseLayers.forEach(layer => MapTools.setMapClip(layer, geoJsonBoundary));
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
### 6. 注册自定义投影
|
|
198
|
+
|
|
199
|
+
```typescript
|
|
200
|
+
import { ProjectionManager } from 'my-openlayer';
|
|
201
|
+
|
|
202
|
+
// 在 MyOl 实例之外注册任意 EPSG
|
|
203
|
+
ProjectionManager.register({
|
|
204
|
+
code: 'EPSG:4528',
|
|
205
|
+
def: '+proj=tmerc +lat_0=0 +lon_0=120 +k=1 +x_0=40500000 +y_0=0 +ellps=GRS80 +units=m +no_defs'
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
// 之后 MyOl 构造时直接用
|
|
209
|
+
const map = new MyOl('map', { projection: { code: 'EPSG:4528' } });
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
### 7. 运行时修改默认配置
|
|
213
|
+
|
|
214
|
+
```typescript
|
|
215
|
+
import { ConfigManager } from 'my-openlayer';
|
|
216
|
+
|
|
217
|
+
// 所有后续 addLine 调用的默认 strokeWidth 变为 4
|
|
218
|
+
ConfigManager.setDefaults('LINE_OPTIONS', { strokeWidth: 4 });
|
|
219
|
+
|
|
220
|
+
// 恢复内置默认
|
|
221
|
+
ConfigManager.resetDefaults('LINE_OPTIONS');
|
|
222
|
+
```
|
|
223
|
+
|
|
154
224
|
## 文档索引
|
|
155
225
|
|
|
156
|
-
|
|
226
|
+
详细文档请访问 **[在线文档](https://cuteyuchen.github.io/my-openlayer/)**,交互式 Demo 请访问 **[Demo 站点](https://cuteyuchen.github.io/my-openlayer/demo/)**。
|
|
157
227
|
|
|
158
228
|
### 核心类库
|
|
159
229
|
- **[MyOl](docs/MyOl.md)**: 地图入口类,负责初始化和模块访问。
|
|
230
|
+
- **[ProjectionManager](docs/ProjectionManager.md)**: 投影管理(3.0 新增),支持 `register()` 注册任意 EPSG。
|
|
160
231
|
- **[MapBaseLayers](docs/MapBaseLayers.md)**: 底图与注记管理。
|
|
161
|
-
- **[ConfigManager](docs/ConfigManager.md)**:
|
|
232
|
+
- **[ConfigManager](docs/ConfigManager.md)**: 配置管理,支持运行时 `setDefaults`。
|
|
162
233
|
- **[EventManager](docs/EventManager.md)**: 事件管理。
|
|
163
|
-
- **[ErrorHandler](docs/ErrorHandler.md)**:
|
|
234
|
+
- **[ErrorHandler](docs/ErrorHandler.md)**: 错误处理,含 `LayerNotFoundError` / `InvalidGeoJSONError` / `ProjectionError`。
|
|
164
235
|
|
|
165
236
|
### 要素操作
|
|
166
|
-
- **[Point](docs/Point.md)**: 点要素(含聚合、DOM
|
|
167
|
-
- **[
|
|
168
|
-
- **[
|
|
237
|
+
- **[Point](docs/Point.md)**: 点要素(含聚合、DOM 点位、高性能闪烁点、统一 Handle)。
|
|
238
|
+
- **[Point](docs/Point.md)**: 点要素(含聚合、DOM 点位、高性能闪烁点、统一 Handle)。
|
|
239
|
+
- **[Line](docs/Line.md)**: 线要素(含静态线、流动线、URL 异步加载)。
|
|
240
|
+
- **[Polygon](docs/Polygon.md)**: 面要素(含热力图、图片层、遮罩层、统一 Handle)。
|
|
169
241
|
- **[VueTemplatePoint](docs/VueTemplatePoint.md)**: Vue 组件点位。
|
|
170
|
-
- **[RiverLayerManager](docs/RiverLayerManager.md)**: 河流图层管理。
|
|
171
242
|
|
|
172
243
|
### 交互与工具
|
|
173
244
|
- **[SelectHandler](docs/SelectHandler.md)**: 要素选择交互(支持独立样式渲染、多选隔离)。
|
|
174
245
|
- **[MeasureHandler](docs/MeasureHandler.md)**: 测量工具。
|
|
175
|
-
- **[MapTools](docs/MapTools.md)**:
|
|
246
|
+
- **[MapTools](docs/MapTools.md)**: 通用地图工具(含 `clipMap` 全图裁剪)。
|
|
176
247
|
- **[ValidationUtils](docs/ValidationUtils.md)**: 验证工具。
|
|
177
248
|
|
|
178
249
|
## 详细用法示例
|
|
@@ -196,7 +267,21 @@ measure.start('LineString'); // 开始测距
|
|
|
196
267
|
measure.end(); // 结束测量
|
|
197
268
|
```
|
|
198
269
|
|
|
199
|
-
|
|
270
|
+
### 要素选择
|
|
271
|
+
|
|
272
|
+
```typescript
|
|
273
|
+
const selectHandler = map.getSelectHandler();
|
|
274
|
+
|
|
275
|
+
selectHandler.enableSelect('click', {
|
|
276
|
+
layerFilter: ['cities'],
|
|
277
|
+
onSelect: (event) => console.log('选中:', event.selected)
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
// 编程式选择
|
|
281
|
+
selectHandler.selectByProperty('name', '杭州', { fitView: true });
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
> 注意:更多示例请访问 [Demo 站点](https://cuteyuchen.github.io/my-openlayer/demo/),每个公开类都有独立的交互式演示页。
|
|
200
285
|
|
|
201
286
|
## 贡献指南
|
|
202
287
|
|
package/core/line/Line.d.ts
CHANGED
|
@@ -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
|
-
|
|
35
|
-
|
|
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
|
|
38
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
return this.
|
|
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');
|
package/core/line/index.d.ts
CHANGED
|
@@ -2,5 +2,3 @@ export { default as Line } from './Line';
|
|
|
2
2
|
export { default as LineFlowAnimator } from './LineFlowAnimator';
|
|
3
3
|
export { default as LineFeatureFactory } from './LineFeatureFactory';
|
|
4
4
|
export { default as LineStyleFactory } from './LineStyleFactory';
|
|
5
|
-
export { default as RiverLayerManager } from './RiverLayerManager';
|
|
6
|
-
export type { RiverLayerOptions, RiverLevelWidthMap } from './RiverLayerManager';
|
package/core/line/index.js
CHANGED
|
@@ -2,4 +2,3 @@ export { default as Line } from './Line';
|
|
|
2
2
|
export { default as LineFlowAnimator } from './LineFlowAnimator';
|
|
3
3
|
export { default as LineFeatureFactory } from './LineFeatureFactory';
|
|
4
4
|
export { default as LineStyleFactory } from './LineStyleFactory';
|
|
5
|
-
export { default as RiverLayerManager } from './RiverLayerManager';
|