@openlayer-utils/draw-grid 1.0.0
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/LICENSE +21 -0
- package/README.md +356 -0
- package/dist/index.cjs +14 -0
- package/dist/index.d.ts +180 -0
- package/dist/index.js +6291 -0
- package/package.json +70 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 loongbao
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,356 @@
|
|
|
1
|
+
# 使用手册
|
|
2
|
+
|
|
3
|
+
## 快速开始
|
|
4
|
+
|
|
5
|
+
### 1. 安装依赖
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pnpm add @openlayer-utils/draw-grid
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
**当前工具不会将ol、vue打包,需要额外安装**
|
|
12
|
+
|
|
13
|
+
### 2. 创建地图
|
|
14
|
+
|
|
15
|
+
```javascript
|
|
16
|
+
import OlMap from 'ol/Map.js';
|
|
17
|
+
import View from 'ol/View.js';
|
|
18
|
+
import TileLayer from 'ol/layer/Tile.js';
|
|
19
|
+
import XYZ from 'ol/source/XYZ.js';
|
|
20
|
+
|
|
21
|
+
const map = new OlMap({
|
|
22
|
+
target: 'map',
|
|
23
|
+
layers: [
|
|
24
|
+
new TileLayer({
|
|
25
|
+
source: new XYZ({ url: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png' }),
|
|
26
|
+
}),
|
|
27
|
+
],
|
|
28
|
+
view: new View({ center: [116.404, 39.915], zoom: 12 }),
|
|
29
|
+
});
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
### 3. 初始化绘制工具
|
|
33
|
+
|
|
34
|
+
```javascript
|
|
35
|
+
import { OpenLayerAdapter, DrawTool } from '@openlayer-utils/draw-grid';
|
|
36
|
+
|
|
37
|
+
const adapter = new OpenLayerAdapter(map);
|
|
38
|
+
const tool = new DrawTool(adapter, {
|
|
39
|
+
insertBiz: (feature) => {
|
|
40
|
+
// 业务侧保存数据,返回 true 表示成功
|
|
41
|
+
return saveToDatabase(feature);
|
|
42
|
+
},
|
|
43
|
+
delBiz: (feature) => {
|
|
44
|
+
// 业务侧删除数据,返回 true 表示成功
|
|
45
|
+
return deleteFromDatabase(feature.id);
|
|
46
|
+
},
|
|
47
|
+
});
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### 4. 开始绘制
|
|
51
|
+
|
|
52
|
+
```javascript
|
|
53
|
+
tool.startDraw({
|
|
54
|
+
type: 'h3', // 网格类型: 'h3' | 'geohash'
|
|
55
|
+
accuracy: 6, // 精度
|
|
56
|
+
drawNeighbor: false, // 是否绘制相邻网格
|
|
57
|
+
lineWidth: 2, // 线条宽度
|
|
58
|
+
primaryColor: '#0000FF', // 颜色
|
|
59
|
+
lineOpacity: 1, // 线条透明度
|
|
60
|
+
fillOpacity: 0.3, // 填充透明度
|
|
61
|
+
enableDel: true, // 允许点击删除
|
|
62
|
+
showLen: true, // 显示边长
|
|
63
|
+
showArea: true, // 显示面积
|
|
64
|
+
debug: false, // 调试模式
|
|
65
|
+
});
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### 5. 绑定地图事件
|
|
69
|
+
|
|
70
|
+
```javascript
|
|
71
|
+
import { toLonLat } from 'ol/proj.js';
|
|
72
|
+
|
|
73
|
+
map.on('dblclick', (evt) => {
|
|
74
|
+
const coord = map.getCoordinateFromPixel(evt.pixel);
|
|
75
|
+
const lonLat = toLonLat(coord, map.getView().getProjection());
|
|
76
|
+
tool.onDblClick(lonLat); // 双击绘制
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
map.on('singleclick', (evt) => {
|
|
80
|
+
const coord = map.getCoordinateFromPixel(evt.pixel);
|
|
81
|
+
const lonLat = toLonLat(coord, map.getView().getProjection());
|
|
82
|
+
tool.onClick(lonLat); // 单击选择/删除
|
|
83
|
+
});
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### 6. 停止绘制
|
|
87
|
+
|
|
88
|
+
```javascript
|
|
89
|
+
tool.stopDraw();
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
---
|
|
93
|
+
|
|
94
|
+
## 交互流程
|
|
95
|
+
|
|
96
|
+
### 绘制流程
|
|
97
|
+
|
|
98
|
+
```
|
|
99
|
+
业务方调用 startDraw() → 进入绘制模式(仅允许绘制一次)
|
|
100
|
+
↓
|
|
101
|
+
用户双击地图 → 触发 onDblClick(坐标)
|
|
102
|
+
↓
|
|
103
|
+
工具生成要素 → 调用 insertBiz 回调
|
|
104
|
+
↓ (返回 true)
|
|
105
|
+
要素添加到 WebGL 图层 → 生成边长/面积标签(如启用)
|
|
106
|
+
↓
|
|
107
|
+
绘制模式自动结束,如需连续绘制需重新调用 startDraw()
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### 删除流程
|
|
111
|
+
|
|
112
|
+
```
|
|
113
|
+
startDraw({ enableDel: true }) → 启用删除
|
|
114
|
+
↓
|
|
115
|
+
用户单击要素 → 触发 onClick(坐标)
|
|
116
|
+
↓
|
|
117
|
+
工具检测命中要素 → 显示删除图标
|
|
118
|
+
↓
|
|
119
|
+
用户单击删除图标 → 弹出 Vue3 确认框
|
|
120
|
+
↓
|
|
121
|
+
确认: delBiz 回调 → 返回 true 则删除要素+标签
|
|
122
|
+
返回 false 则还原要素
|
|
123
|
+
取消: 不做任何操作
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
---
|
|
127
|
+
|
|
128
|
+
## API 设计
|
|
129
|
+
|
|
130
|
+
### DrawTool
|
|
131
|
+
|
|
132
|
+
核心绘制工具类。
|
|
133
|
+
|
|
134
|
+
```typescript
|
|
135
|
+
class DrawTool {
|
|
136
|
+
readonly drawEnabled: boolean;
|
|
137
|
+
|
|
138
|
+
constructor(adapter: IMapAdapter, callbacks?: DrawCallbacks);
|
|
139
|
+
|
|
140
|
+
startDraw(options?: DrawOptions): void;
|
|
141
|
+
stopDraw(): void;
|
|
142
|
+
onDblClick(coordinate: [number, number]): void;
|
|
143
|
+
onClick(coordinate: [number, number]): void;
|
|
144
|
+
getFeatures(): FeatureInfo[];
|
|
145
|
+
getOptions(): Readonly<Required<Omit<DrawOptions, 'oldFeatures'>>>;
|
|
146
|
+
}
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
| 方法 | 说明 |
|
|
150
|
+
|------|------|
|
|
151
|
+
| `startDraw(options)` | 开始绘制模式,创建图层并配置参数。每次调用仅允许一次绘制,绘后自动结束 |
|
|
152
|
+
| `stopDraw()` | 停止绘制,销毁图层及所有要素、标签、弹窗 |
|
|
153
|
+
| `onDblClick([lng, lat])` | 处理双击事件,在点击位置生成网格要素 |
|
|
154
|
+
| `onClick([lng, lat])` | 处理单击事件,检测要素命中并触发删除交互 |
|
|
155
|
+
| `getFeatures()` | 返回当前图层中所有要素的副本 |
|
|
156
|
+
| `drawEnabled` | 只读,当前是否处于可绘制状态 |
|
|
157
|
+
|
|
158
|
+
### DrawOptions
|
|
159
|
+
|
|
160
|
+
```typescript
|
|
161
|
+
interface DrawOptions {
|
|
162
|
+
type?: GridType; // 网格类型,默认 'h3'
|
|
163
|
+
accuracy?: number; // 精度,H3:1-15, GeoHash:1-12,默认 6
|
|
164
|
+
drawNeighbor?: boolean; // 是否绘制相邻网格,默认 false
|
|
165
|
+
lineWidth?: number; // 线条宽度(px),默认 2
|
|
166
|
+
primaryColor?: string; // 主题色(CSS颜色),默认 '#0000FF'
|
|
167
|
+
lineOpacity?: number; // 线条透明度 0-1,默认 1
|
|
168
|
+
fillOpacity?: number; // 填充透明度 0-1,默认 0.3
|
|
169
|
+
enableDel?: boolean; // 是否允许删除,默认 false
|
|
170
|
+
showLen?: boolean; // 是否展示边长,默认 false
|
|
171
|
+
showArea?: boolean; // 是否展示面积,默认 false
|
|
172
|
+
debug?: boolean; // 调试模式,控制台打印日志,默认 false
|
|
173
|
+
oldFeatures?: FeatureInfo[]; // 已有要素列表
|
|
174
|
+
}
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
### DrawCallbacks
|
|
178
|
+
|
|
179
|
+
```typescript
|
|
180
|
+
interface DrawCallbacks {
|
|
181
|
+
insertBiz?: (info: FeatureInfo) => boolean;
|
|
182
|
+
delBiz?: (info: FeatureInfo) => boolean;
|
|
183
|
+
}
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
| 回调 | 说明 |
|
|
187
|
+
|------|------|
|
|
188
|
+
| `insertBiz(feature)` | 新增要素时调用。返回 `true` 要素生效,`false` 取消绘制 |
|
|
189
|
+
| `delBiz(feature)` | 删除要素时调用。返回 `true` 删除生效,`false` 还原要素 |
|
|
190
|
+
|
|
191
|
+
### FeatureInfo
|
|
192
|
+
|
|
193
|
+
```typescript
|
|
194
|
+
interface FeatureInfo {
|
|
195
|
+
id: number;
|
|
196
|
+
type: GridType;
|
|
197
|
+
grids: GridInfo[];
|
|
198
|
+
drawNeighbor: boolean;
|
|
199
|
+
accuracy: number;
|
|
200
|
+
primaryColor: string;
|
|
201
|
+
lineWidth: number;
|
|
202
|
+
lineOpacity: number;
|
|
203
|
+
fillOpacity: number;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
interface GridInfo {
|
|
207
|
+
primary: boolean;
|
|
208
|
+
gridKey: string;
|
|
209
|
+
wkt: string;
|
|
210
|
+
locationType: LocationType;
|
|
211
|
+
}
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
### 枚举
|
|
215
|
+
|
|
216
|
+
```typescript
|
|
217
|
+
enum GridType {
|
|
218
|
+
H3 = 'h3',
|
|
219
|
+
GEOHASH = 'geohash',
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
enum LocationType {
|
|
223
|
+
CENTER = 'CENTER',
|
|
224
|
+
NORTH = 'NORTH',
|
|
225
|
+
SOUTH = 'SOUTH',
|
|
226
|
+
EAST = 'EAST',
|
|
227
|
+
WEST = 'WEST',
|
|
228
|
+
NORTHEAST = 'NORTHEAST',
|
|
229
|
+
NORTHWEST = 'NORTHWEST',
|
|
230
|
+
SOUTHEAST = 'SOUTHEAST',
|
|
231
|
+
SOUTHWEST = 'SOUTHWEST',
|
|
232
|
+
}
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
### IMapAdapter(适配器接口)
|
|
236
|
+
|
|
237
|
+
```typescript
|
|
238
|
+
interface ILayerHandle {
|
|
239
|
+
readonly id: symbol;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
interface IMapAdapter {
|
|
243
|
+
readonly mapInstance: unknown;
|
|
244
|
+
|
|
245
|
+
createLayer(): ILayerHandle;
|
|
246
|
+
addFeature(layer: ILayerHandle, feature: FeatureInfo): void;
|
|
247
|
+
removeFeature(layer: ILayerHandle, featureId: number): void;
|
|
248
|
+
clearLayer(layer: ILayerHandle): void;
|
|
249
|
+
destroyLayer(layer: ILayerHandle): void;
|
|
250
|
+
findFeatureIdAtCoordinate(layer: ILayerHandle, coordinate: [number, number]): number | null;
|
|
251
|
+
showDeleteWithConfirm(layer: ILayerHandle, feature: FeatureInfo, coordinate: [number, number]): Promise<boolean>;
|
|
252
|
+
hideDeleteIcon(layer: ILayerHandle): void;
|
|
253
|
+
showFeatureLabel(layer: ILayerHandle, featureId: number, text: string, coordinate: [number, number], labelKind?: string, color?: string): void;
|
|
254
|
+
hideFeatureLabel(layer: ILayerHandle, featureId: number, labelKind?: string): void;
|
|
255
|
+
}
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
### GridStrategy(网格策略接口)
|
|
259
|
+
|
|
260
|
+
```typescript
|
|
261
|
+
interface GridStrategy {
|
|
262
|
+
readonly type: GridType;
|
|
263
|
+
latLngToGridKey(lng: number, lat: number, accuracy: number): string;
|
|
264
|
+
getNeighbors(gridKey: string, accuracy: number): NeighborInfo[];
|
|
265
|
+
gridKeyToWKT(gridKey: string): string;
|
|
266
|
+
calculateEdgeLength(gridKey: string, accuracy: number): number;
|
|
267
|
+
calculateArea(gridKey: string, accuracy: number): number;
|
|
268
|
+
formatLength(meters: number): string;
|
|
269
|
+
formatArea(squareMeters: number): string;
|
|
270
|
+
buildFeature(id, lng, lat, accuracy, drawNeighbor, primaryColor, lineWidth, lineOpacity, fillOpacity): FeatureInfo;
|
|
271
|
+
}
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
通过工厂函数获取策略实例:
|
|
275
|
+
|
|
276
|
+
```typescript
|
|
277
|
+
import { getGridStrategy, GridType } from '@openlayer-utils/draw-grid';
|
|
278
|
+
const h3Strategy = getGridStrategy(GridType.H3);
|
|
279
|
+
const key = h3Strategy.latLngToGridKey(116.404, 39.915, 6);
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
### OpenLayerAdapter
|
|
283
|
+
|
|
284
|
+
```typescript
|
|
285
|
+
import { OpenLayerAdapter } from '@openlayer-utils/draw-grid';
|
|
286
|
+
|
|
287
|
+
const adapter = new OpenLayerAdapter(map); // map: ol/Map 实例
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
基于 WebGLVectorLayer 的高性能渲染适配器,实现 IMapAdapter 的全部方法。
|
|
291
|
+
|
|
292
|
+
---
|
|
293
|
+
|
|
294
|
+
## 整体架构设计
|
|
295
|
+
|
|
296
|
+
### 分层架构
|
|
297
|
+
|
|
298
|
+
```
|
|
299
|
+
┌─────────────────────────────────────────┐
|
|
300
|
+
│ 业务集成方 │
|
|
301
|
+
│ (传入 Map 实例、事件、回调) │
|
|
302
|
+
└──────────────┬──────────────────────────┘
|
|
303
|
+
│
|
|
304
|
+
┌──────────────▼──────────────────────────┐
|
|
305
|
+
│ DrawTool (core) │
|
|
306
|
+
│ 核心绘制逻辑、要素管理、去重、ID生成 │
|
|
307
|
+
└──────┬────────────────────┬─────────────┘
|
|
308
|
+
│ │
|
|
309
|
+
┌──────▼──────┐ ┌────────▼──────────────┐
|
|
310
|
+
│ GridStrategy │ │ IMapAdapter │
|
|
311
|
+
│ (策略模式) │ │ (适配器接口) │
|
|
312
|
+
│ H3 / GeoHash│ │ createLayer() │
|
|
313
|
+
│ 网格计算 │ │ addFeature() │
|
|
314
|
+
│ WKT 生成 │ │ removeFeature() │
|
|
315
|
+
│ 边/面积计算 │ │ showDeleteWithConfirm│
|
|
316
|
+
└──────────────┘ │ ... │
|
|
317
|
+
└────────┬─────────────┘
|
|
318
|
+
│
|
|
319
|
+
┌────────▼─────────────┐
|
|
320
|
+
│ OpenLayerAdapter │
|
|
321
|
+
│ WebGLVectorLayer │
|
|
322
|
+
│ OL Overlay / Style │
|
|
323
|
+
└───────────────────────┘
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
### 核心模块职责
|
|
327
|
+
|
|
328
|
+
| 模块 | 位置 | 职责 |
|
|
329
|
+
|------|---------------------|------|
|
|
330
|
+
| `DrawTool` | `packages/src/core` | 绘制生命周期管理、要素增删、事件处理 |
|
|
331
|
+
| `GridStrategy` | `packages/src/core` | H3/GeoHash 网格的坐标转换、邻居计算、WKT生成 |
|
|
332
|
+
| `IMapAdapter` | `packages/src/core` | 定义 GIS 框架适配器的接口契约 |
|
|
333
|
+
| `OpenLayerAdapter` | `packages/src` | 实现 IMapAdapter,对接 OpenLayer WebGLVectorLayer |
|
|
334
|
+
|
|
335
|
+
### 设计模式
|
|
336
|
+
|
|
337
|
+
**Adapter 模式** — 核心层定义 `IMapAdapter` 接口,各 GIS 框架通过实现该接口完成适配。当前支持 OpenLayer,后续可扩展 MapLibre、Cesium 等
|
|
338
|
+
|
|
339
|
+
**Strategy 模式** — `GridStrategy` 抽象 H3 和 GeoHash 的网格计算差异,通过 `GridStrategyFactory` 按类型获取对应策略实例
|
|
340
|
+
|
|
341
|
+
### 坐标系统透明化
|
|
342
|
+
|
|
343
|
+
核心工具层(DrawTool / GridStrategy)对坐标系统完全透明:
|
|
344
|
+
- 所有坐标以 `[number, number]` 透传,不做任何投影变换
|
|
345
|
+
- 投影相关处理由适配器层(OpenLayerAdapter)在读写数据时根据地图实例动态完成
|
|
346
|
+
|
|
347
|
+
---
|
|
348
|
+
|
|
349
|
+
## 注意事项
|
|
350
|
+
|
|
351
|
+
1. **多实例**: DrawTool 支持并行实例化,每个实例独立管理图层
|
|
352
|
+
2. **去重**: 同一类型、同一精度的网格不允许完全重叠绘制
|
|
353
|
+
3. **ID 生成**: 工具自动生成全局唯一的 Number 类型 ID
|
|
354
|
+
4. **oldFeatures**: 支持传入已有要素,其 ID 不会被修改
|
|
355
|
+
5. **删除确认**: 删除确认框由工具负责维护,基于 Vue 3 实现
|
|
356
|
+
6. **单次绘制**: 每次 startDraw() 只允许一次绘制,绘后自动结束,连续绘制需重复调用
|