my-openlayer 2.0.1 → 2.1.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.
- package/MyOl.d.ts +7 -0
- package/MyOl.js +19 -0
- package/core/Line.d.ts +9 -9
- package/core/Line.js +224 -66
- package/core/Polygon.d.ts +6 -6
- package/core/Polygon.js +91 -44
- package/core/SelectHandler.d.ts +147 -0
- package/core/SelectHandler.js +445 -0
- package/index.d.ts +2 -1
- package/index.js +1 -0
- package/package.json +1 -1
- package/types.d.ts +35 -0
package/MyOl.d.ts
CHANGED
|
@@ -5,6 +5,7 @@ import Point from "./core/Point";
|
|
|
5
5
|
import Line from "./core/Line";
|
|
6
6
|
import MapBaseLayers from "./core/MapBaseLayers";
|
|
7
7
|
import MapTools from "./core/MapTools";
|
|
8
|
+
import SelectHandler from "./core/SelectHandler";
|
|
8
9
|
import { ErrorHandler } from './utils/ErrorHandler';
|
|
9
10
|
import { EventManager } from './core/EventManager';
|
|
10
11
|
import { ConfigManager } from './core/ConfigManager';
|
|
@@ -20,6 +21,7 @@ export default class MyOl {
|
|
|
20
21
|
private _mapTools?;
|
|
21
22
|
private _point?;
|
|
22
23
|
private _line?;
|
|
24
|
+
private _selectHandler?;
|
|
23
25
|
private readonly errorHandler;
|
|
24
26
|
private _eventManager?;
|
|
25
27
|
private readonly configManager;
|
|
@@ -83,6 +85,11 @@ export default class MyOl {
|
|
|
83
85
|
* @returns Line 线要素操作实例
|
|
84
86
|
*/
|
|
85
87
|
getLine(): Line;
|
|
88
|
+
/**
|
|
89
|
+
* 获取要素选择处理器模块
|
|
90
|
+
* @returns SelectHandler 要素选择处理器实例
|
|
91
|
+
*/
|
|
92
|
+
getSelectHandler(): SelectHandler;
|
|
86
93
|
/**
|
|
87
94
|
* 获取地图工具模块
|
|
88
95
|
* @returns MapTools 地图工具实例
|
package/MyOl.js
CHANGED
|
@@ -12,6 +12,7 @@ import Point from "./core/Point";
|
|
|
12
12
|
import Line from "./core/Line";
|
|
13
13
|
import MapBaseLayers from "./core/MapBaseLayers";
|
|
14
14
|
import MapTools from "./core/MapTools";
|
|
15
|
+
import SelectHandler from "./core/SelectHandler";
|
|
15
16
|
import { ErrorHandler, MyOpenLayersError, ErrorType } from './utils/ErrorHandler';
|
|
16
17
|
import { EventManager } from './core/EventManager';
|
|
17
18
|
import { ConfigManager } from './core/ConfigManager';
|
|
@@ -240,6 +241,23 @@ class MyOl {
|
|
|
240
241
|
throw error;
|
|
241
242
|
}
|
|
242
243
|
}
|
|
244
|
+
/**
|
|
245
|
+
* 获取要素选择处理器模块
|
|
246
|
+
* @returns SelectHandler 要素选择处理器实例
|
|
247
|
+
*/
|
|
248
|
+
getSelectHandler() {
|
|
249
|
+
try {
|
|
250
|
+
if (!this._selectHandler) {
|
|
251
|
+
this._selectHandler = new SelectHandler(this.map);
|
|
252
|
+
console.debug('要素选择模块已加载');
|
|
253
|
+
}
|
|
254
|
+
return this._selectHandler;
|
|
255
|
+
}
|
|
256
|
+
catch (error) {
|
|
257
|
+
this.errorHandler.handleError(new MyOpenLayersError('要素选择模块初始化失败', ErrorType.COMPONENT_ERROR, { error }));
|
|
258
|
+
throw error;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
243
261
|
/**
|
|
244
262
|
* 获取地图工具模块
|
|
245
263
|
* @returns MapTools 地图工具实例
|
|
@@ -358,6 +376,7 @@ class MyOl {
|
|
|
358
376
|
this._polygon = undefined;
|
|
359
377
|
this._mapTools = undefined;
|
|
360
378
|
this._baseLayers = undefined;
|
|
379
|
+
this._selectHandler = undefined;
|
|
361
380
|
// 销毁地图
|
|
362
381
|
this.map.setTarget(undefined);
|
|
363
382
|
console.debug('地图实例已销毁', { map: this.map });
|
package/core/Line.d.ts
CHANGED
|
@@ -52,33 +52,33 @@ export default class Line {
|
|
|
52
52
|
*/
|
|
53
53
|
constructor(map: Map);
|
|
54
54
|
/**
|
|
55
|
-
*
|
|
55
|
+
* 添加线要素
|
|
56
56
|
* @param data GeoJSON格式的线数据
|
|
57
57
|
* @param options 配置项
|
|
58
58
|
* @returns 创建的矢量图层
|
|
59
59
|
*/
|
|
60
60
|
addLine(data: MapJSONData, options?: LineOptions): VectorLayer<VectorSource>;
|
|
61
61
|
/**
|
|
62
|
-
*
|
|
62
|
+
* 从URL添加线要素
|
|
63
63
|
* @param url 数据URL
|
|
64
64
|
* @param options 配置项
|
|
65
65
|
* @returns 创建的矢量图层
|
|
66
66
|
*/
|
|
67
|
-
|
|
67
|
+
addLineByUrl(url: string, options?: LineOptions): VectorLayer<VectorSource>;
|
|
68
68
|
/**
|
|
69
|
-
*
|
|
69
|
+
* 添加分级河流图层,根据缩放级别显示不同级别的河流
|
|
70
70
|
* @param fyRiverJson 河流 GeoJSON 数据
|
|
71
71
|
* @param options 河流图层配置选项
|
|
72
72
|
* @throws {Error} 当数据格式无效时抛出错误
|
|
73
73
|
*/
|
|
74
74
|
addRiverLayersByZoom(fyRiverJson: MapJSONData, options?: RiverLayerOptions): void;
|
|
75
75
|
/**
|
|
76
|
-
*
|
|
76
|
+
* 从URL添加分级河流图层,根据缩放级别显示不同级别的河流
|
|
77
77
|
* @param url 河流数据URL
|
|
78
78
|
* @param options 河流图层配置选项
|
|
79
79
|
* @throws {Error} 当数据格式无效时抛出错误
|
|
80
80
|
*/
|
|
81
|
-
|
|
81
|
+
addRiverLayersByZoomByUrl(url: string, options?: RiverLayerOptions): void;
|
|
82
82
|
/**
|
|
83
83
|
* 显示或隐藏河流图层
|
|
84
84
|
* @param show 是否显示河流图层
|
|
@@ -90,19 +90,19 @@ export default class Line {
|
|
|
90
90
|
*/
|
|
91
91
|
showRiverLayerByZoom(): void;
|
|
92
92
|
/**
|
|
93
|
-
*
|
|
93
|
+
* 添加按级别显示不同宽度的河流图层
|
|
94
94
|
* @param data 河流 GeoJSON 数据
|
|
95
95
|
* @param options 河流图层配置选项
|
|
96
96
|
* @returns 创建的河流图层
|
|
97
97
|
*/
|
|
98
98
|
addRiverWidthByLevel(data: MapJSONData, options?: RiverLayerOptions): VectorLayer<VectorSource>;
|
|
99
99
|
/**
|
|
100
|
-
*
|
|
100
|
+
* 从URL添加按级别显示不同宽度的河流图层
|
|
101
101
|
* @param url 河流数据URL
|
|
102
102
|
* @param options 河流图层配置选项
|
|
103
103
|
* @returns 创建的河流图层
|
|
104
104
|
*/
|
|
105
|
-
|
|
105
|
+
addRiverWidthByLevelByUrl(url: string, options?: RiverLayerOptions): VectorLayer<VectorSource>;
|
|
106
106
|
/**
|
|
107
107
|
* 移除线图层
|
|
108
108
|
* @param layerName 图层名称
|
package/core/Line.js
CHANGED
|
@@ -43,10 +43,7 @@ export default class Line {
|
|
|
43
43
|
this.eventManager = new EventManager(map);
|
|
44
44
|
}
|
|
45
45
|
addLine(data, options = {}) {
|
|
46
|
-
|
|
47
|
-
if (!isUrl) {
|
|
48
|
-
ValidationUtils.validateGeoJSONData(data);
|
|
49
|
-
}
|
|
46
|
+
ValidationUtils.validateGeoJSONData(data);
|
|
50
47
|
const defaultOptions = {
|
|
51
48
|
type: 'line',
|
|
52
49
|
strokeColor: 'rgba(3, 122, 255, 1)',
|
|
@@ -56,15 +53,62 @@ export default class Line {
|
|
|
56
53
|
layerName: options.layerName || 'lineLayer'
|
|
57
54
|
};
|
|
58
55
|
const mergedOptions = { ...defaultOptions, ...options };
|
|
59
|
-
|
|
60
|
-
const
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
}
|
|
65
|
-
: new VectorSource({
|
|
66
|
-
|
|
67
|
-
|
|
56
|
+
const features = new GeoJSON().readFeatures(data, options.projectionOptOptions);
|
|
57
|
+
const layer = new VectorLayer({
|
|
58
|
+
properties: {
|
|
59
|
+
name: mergedOptions.layerName,
|
|
60
|
+
layerName: mergedOptions.layerName
|
|
61
|
+
},
|
|
62
|
+
source: new VectorSource({ features }),
|
|
63
|
+
style: (feature) => {
|
|
64
|
+
if (feature instanceof Feature) {
|
|
65
|
+
feature.set('type', mergedOptions.type);
|
|
66
|
+
feature.set('layerName', mergedOptions.type);
|
|
67
|
+
}
|
|
68
|
+
// 如果传入了自定义样式,直接使用
|
|
69
|
+
if (mergedOptions.style) {
|
|
70
|
+
if (typeof mergedOptions.style === 'function') {
|
|
71
|
+
return mergedOptions.style(feature);
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
return mergedOptions.style;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return new Style({
|
|
78
|
+
stroke: new Stroke({
|
|
79
|
+
color: mergedOptions.strokeColor,
|
|
80
|
+
width: mergedOptions.strokeWidth,
|
|
81
|
+
lineDash: mergedOptions.lineDash,
|
|
82
|
+
lineDashOffset: mergedOptions.lineDashOffset
|
|
83
|
+
})
|
|
84
|
+
});
|
|
85
|
+
},
|
|
86
|
+
zIndex: mergedOptions.zIndex
|
|
87
|
+
});
|
|
88
|
+
layer.setVisible(mergedOptions.visible);
|
|
89
|
+
this.map.addLayer(layer);
|
|
90
|
+
return layer;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* 从URL添加线要素
|
|
94
|
+
* @param url 数据URL
|
|
95
|
+
* @param options 配置项
|
|
96
|
+
* @returns 创建的矢量图层
|
|
97
|
+
*/
|
|
98
|
+
addLineByUrl(url, options = {}) {
|
|
99
|
+
const defaultOptions = {
|
|
100
|
+
type: 'line',
|
|
101
|
+
strokeColor: 'rgba(3, 122, 255, 1)',
|
|
102
|
+
strokeWidth: 2,
|
|
103
|
+
visible: true,
|
|
104
|
+
zIndex: 15,
|
|
105
|
+
layerName: options.layerName || 'lineLayer'
|
|
106
|
+
};
|
|
107
|
+
const mergedOptions = { ...defaultOptions, ...options };
|
|
108
|
+
const source = new VectorSource({
|
|
109
|
+
url,
|
|
110
|
+
format: new GeoJSON(options.projectionOptOptions)
|
|
111
|
+
});
|
|
68
112
|
const layer = new VectorLayer({
|
|
69
113
|
properties: {
|
|
70
114
|
name: mergedOptions.layerName,
|
|
@@ -101,10 +145,7 @@ export default class Line {
|
|
|
101
145
|
return layer;
|
|
102
146
|
}
|
|
103
147
|
addRiverLayersByZoom(fyRiverJson, options = {}) {
|
|
104
|
-
|
|
105
|
-
if (!isUrl) {
|
|
106
|
-
ValidationUtils.validateGeoJSONData(fyRiverJson);
|
|
107
|
-
}
|
|
148
|
+
ValidationUtils.validateGeoJSONData(fyRiverJson);
|
|
108
149
|
const defaultOptions = {
|
|
109
150
|
type: 'river',
|
|
110
151
|
levelCount: 5,
|
|
@@ -126,44 +167,106 @@ export default class Line {
|
|
|
126
167
|
this.riverLayerList = [];
|
|
127
168
|
// 创建分级河流图层
|
|
128
169
|
for (let level = 1; level <= mergedOptions.levelCount; level++) {
|
|
129
|
-
const vectorSource =
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
if (feature.properties && feature.properties.level === level) {
|
|
140
|
-
try {
|
|
141
|
-
const olFeature = geojson.readFeature(feature);
|
|
142
|
-
if (Array.isArray(olFeature)) {
|
|
143
|
-
vectorSource.addFeatures(olFeature);
|
|
144
|
-
}
|
|
145
|
-
else {
|
|
146
|
-
vectorSource.addFeature(olFeature);
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
catch (error) {
|
|
150
|
-
console.warn(`Failed to load river feature at level ${level}:`, error);
|
|
151
|
-
}
|
|
170
|
+
const vectorSource = new VectorSource({
|
|
171
|
+
format: new GeoJSON(),
|
|
172
|
+
loader: () => {
|
|
173
|
+
const geojson = new GeoJSON();
|
|
174
|
+
fyRiverJson.features.forEach((feature) => {
|
|
175
|
+
if (feature.properties && feature.properties.level === level) {
|
|
176
|
+
try {
|
|
177
|
+
const olFeature = geojson.readFeature(feature);
|
|
178
|
+
if (Array.isArray(olFeature)) {
|
|
179
|
+
vectorSource.addFeatures(olFeature);
|
|
152
180
|
}
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
181
|
+
else {
|
|
182
|
+
vectorSource.addFeature(olFeature);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
catch (error) {
|
|
186
|
+
console.warn(`Failed to load river feature at level ${level}:`, error);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
});
|
|
192
|
+
const riverLayer = new VectorLayer({
|
|
193
|
+
properties: {
|
|
194
|
+
name: mergedOptions.layerName,
|
|
195
|
+
layerName: mergedOptions.layerName,
|
|
196
|
+
riverLevel: level
|
|
197
|
+
},
|
|
198
|
+
source: vectorSource,
|
|
199
|
+
style: (feature) => {
|
|
200
|
+
if (feature instanceof Feature) {
|
|
201
|
+
feature.set('type', mergedOptions.layerName);
|
|
202
|
+
feature.set('layerName', mergedOptions.layerName);
|
|
203
|
+
}
|
|
204
|
+
// 如果传入了自定义样式,直接使用
|
|
205
|
+
if (mergedOptions.style) {
|
|
206
|
+
if (typeof mergedOptions.style === 'function') {
|
|
207
|
+
return mergedOptions.style(feature);
|
|
208
|
+
}
|
|
209
|
+
else {
|
|
210
|
+
return mergedOptions.style;
|
|
211
|
+
}
|
|
160
212
|
}
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
213
|
+
return new Style({
|
|
214
|
+
stroke: new Stroke({
|
|
215
|
+
color: mergedOptions.strokeColor,
|
|
216
|
+
width: mergedOptions.strokeWidth
|
|
217
|
+
})
|
|
218
|
+
});
|
|
219
|
+
},
|
|
220
|
+
zIndex: mergedOptions.zIndex
|
|
221
|
+
});
|
|
222
|
+
riverLayer.setVisible(false);
|
|
223
|
+
this.riverLayerList.push(riverLayer);
|
|
224
|
+
this.map.addLayer(riverLayer);
|
|
225
|
+
}
|
|
226
|
+
// 设置缩放事件监听
|
|
227
|
+
this.eventManager.on('moveend', () => {
|
|
228
|
+
this.showRiverLayerByZoom();
|
|
229
|
+
});
|
|
230
|
+
// 初始显示
|
|
231
|
+
this.showRiverLayerByZoom();
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* 从URL添加分级河流图层,根据缩放级别显示不同级别的河流
|
|
235
|
+
* @param url 河流数据URL
|
|
236
|
+
* @param options 河流图层配置选项
|
|
237
|
+
* @throws {Error} 当数据格式无效时抛出错误
|
|
238
|
+
*/
|
|
239
|
+
addRiverLayersByZoomByUrl(url, options = {}) {
|
|
240
|
+
const defaultOptions = {
|
|
241
|
+
type: 'river',
|
|
242
|
+
levelCount: 5,
|
|
243
|
+
zoomOffset: 8,
|
|
244
|
+
strokeColor: 'rgb(0,113,255)',
|
|
245
|
+
strokeWidth: 3,
|
|
246
|
+
visible: true,
|
|
247
|
+
zIndex: 15,
|
|
248
|
+
layerName: 'riverLayer',
|
|
249
|
+
removeExisting: options.removeExisting ?? false,
|
|
250
|
+
levelWidthMap: this.defaultLevelWidthMap
|
|
251
|
+
};
|
|
252
|
+
const mergedOptions = { ...defaultOptions, ...options };
|
|
253
|
+
// 清除现有河流图层
|
|
254
|
+
if (mergedOptions.removeExisting) {
|
|
255
|
+
this.clearRiverLayers();
|
|
256
|
+
}
|
|
257
|
+
this.riverLayerShow = mergedOptions.visible;
|
|
258
|
+
this.riverLayerList = [];
|
|
259
|
+
// 创建分级河流图层
|
|
260
|
+
for (let level = 1; level <= mergedOptions.levelCount; level++) {
|
|
261
|
+
const vectorSource = new VectorSource({
|
|
262
|
+
url,
|
|
263
|
+
format: new GeoJSON(),
|
|
264
|
+
loader: function (extent, resolution, projection, success, failure) {
|
|
265
|
+
fetch(url)
|
|
266
|
+
.then(response => response.json())
|
|
267
|
+
.then(data => {
|
|
165
268
|
const geojson = new GeoJSON();
|
|
166
|
-
|
|
269
|
+
data.features.forEach((feature) => {
|
|
167
270
|
if (feature.properties && feature.properties.level === level) {
|
|
168
271
|
try {
|
|
169
272
|
const olFeature = geojson.readFeature(feature);
|
|
@@ -179,8 +282,14 @@ export default class Line {
|
|
|
179
282
|
}
|
|
180
283
|
}
|
|
181
284
|
});
|
|
182
|
-
|
|
183
|
-
|
|
285
|
+
success?.(vectorSource.getFeatures());
|
|
286
|
+
})
|
|
287
|
+
.catch(error => {
|
|
288
|
+
console.error('Error loading river data:', error);
|
|
289
|
+
failure?.();
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
});
|
|
184
293
|
const riverLayer = new VectorLayer({
|
|
185
294
|
properties: {
|
|
186
295
|
name: mergedOptions.layerName,
|
|
@@ -251,10 +360,64 @@ export default class Line {
|
|
|
251
360
|
});
|
|
252
361
|
}
|
|
253
362
|
addRiverWidthByLevel(data, options = {}) {
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
363
|
+
ValidationUtils.validateGeoJSONData(data);
|
|
364
|
+
// 合并默认配置
|
|
365
|
+
const mergedOptions = {
|
|
366
|
+
type: 'river',
|
|
367
|
+
layerName: 'river',
|
|
368
|
+
strokeColor: 'rgba(3, 122, 255, 1)',
|
|
369
|
+
strokeWidth: 2,
|
|
370
|
+
visible: true,
|
|
371
|
+
zIndex: 15,
|
|
372
|
+
levelWidthMap: this.defaultLevelWidthMap,
|
|
373
|
+
removeExisting: options.removeExisting ?? false,
|
|
374
|
+
...options
|
|
375
|
+
};
|
|
376
|
+
// 移除同名图层(如果存在)
|
|
377
|
+
if (mergedOptions.removeExisting && mergedOptions.layerName) {
|
|
378
|
+
MapTools.removeLayer(this.map, mergedOptions.layerName);
|
|
257
379
|
}
|
|
380
|
+
// 解析 GeoJSON 数据
|
|
381
|
+
const features = new GeoJSON().readFeatures(data, options.projectionOptOptions);
|
|
382
|
+
// 创建河流图层
|
|
383
|
+
const riverLayer = new VectorLayer({
|
|
384
|
+
properties: {
|
|
385
|
+
name: mergedOptions.layerName,
|
|
386
|
+
layerName: mergedOptions.layerName
|
|
387
|
+
},
|
|
388
|
+
source: new VectorSource({ features }),
|
|
389
|
+
style: (feature) => {
|
|
390
|
+
// 如果传入了自定义样式,直接使用
|
|
391
|
+
if (mergedOptions.style) {
|
|
392
|
+
if (typeof mergedOptions.style === 'function') {
|
|
393
|
+
return mergedOptions.style(feature);
|
|
394
|
+
}
|
|
395
|
+
else {
|
|
396
|
+
return mergedOptions.style;
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
const level = feature.get('level');
|
|
400
|
+
const levelWidth = mergedOptions.levelWidthMap[Number(level)] || 1;
|
|
401
|
+
return new Style({
|
|
402
|
+
stroke: new Stroke({
|
|
403
|
+
color: mergedOptions.strokeColor,
|
|
404
|
+
width: levelWidth
|
|
405
|
+
})
|
|
406
|
+
});
|
|
407
|
+
},
|
|
408
|
+
zIndex: mergedOptions.zIndex
|
|
409
|
+
});
|
|
410
|
+
riverLayer.setVisible(mergedOptions.visible);
|
|
411
|
+
this.map.addLayer(riverLayer);
|
|
412
|
+
return riverLayer;
|
|
413
|
+
}
|
|
414
|
+
/**
|
|
415
|
+
* 从URL添加按级别显示不同宽度的河流图层
|
|
416
|
+
* @param url 河流数据URL
|
|
417
|
+
* @param options 河流图层配置选项
|
|
418
|
+
* @returns 创建的河流图层
|
|
419
|
+
*/
|
|
420
|
+
addRiverWidthByLevelByUrl(url, options = {}) {
|
|
258
421
|
// 合并默认配置
|
|
259
422
|
const mergedOptions = {
|
|
260
423
|
type: 'river',
|
|
@@ -271,15 +434,10 @@ export default class Line {
|
|
|
271
434
|
if (mergedOptions.removeExisting && mergedOptions.layerName) {
|
|
272
435
|
MapTools.removeLayer(this.map, mergedOptions.layerName);
|
|
273
436
|
}
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
format: new GeoJSON(options.projectionOptOptions)
|
|
279
|
-
})
|
|
280
|
-
: new VectorSource({
|
|
281
|
-
features: new GeoJSON().readFeatures(data, options.projectionOptOptions)
|
|
282
|
-
});
|
|
437
|
+
const source = new VectorSource({
|
|
438
|
+
url,
|
|
439
|
+
format: new GeoJSON(options.projectionOptOptions)
|
|
440
|
+
});
|
|
283
441
|
// 创建河流图层
|
|
284
442
|
const riverLayer = new VectorLayer({
|
|
285
443
|
properties: {
|
package/core/Polygon.d.ts
CHANGED
|
@@ -25,7 +25,7 @@ export default class Polygon {
|
|
|
25
25
|
*/
|
|
26
26
|
getLevColor(lev: string | number): string;
|
|
27
27
|
/**
|
|
28
|
-
*
|
|
28
|
+
* 添加地图边框图层
|
|
29
29
|
* @param data 图层数据,必须是有效的 GeoJSON 格式
|
|
30
30
|
* @param options 图层配置选项
|
|
31
31
|
* @returns 创建的图层实例
|
|
@@ -33,15 +33,15 @@ export default class Polygon {
|
|
|
33
33
|
*/
|
|
34
34
|
addBorderPolygon(data: MapJSONData, options?: PolygonOptions): VectorLayer<VectorSource>;
|
|
35
35
|
/**
|
|
36
|
-
*
|
|
36
|
+
* 从URL添加地图边框图层
|
|
37
37
|
* @param url 数据URL
|
|
38
38
|
* @param options 图层配置选项
|
|
39
39
|
* @returns 创建的图层实例
|
|
40
40
|
* @throws 当数据格式无效时抛出错误
|
|
41
41
|
*/
|
|
42
|
-
|
|
42
|
+
addBorderPolygonByUrl(url: string, options?: PolygonOptions): VectorLayer<VectorSource>;
|
|
43
43
|
/**
|
|
44
|
-
*
|
|
44
|
+
* 添加多边形图层
|
|
45
45
|
* @param dataJSON GeoJSON 数据
|
|
46
46
|
* @param options 图层配置选项
|
|
47
47
|
* @returns 创建的矢量图层
|
|
@@ -49,13 +49,13 @@ export default class Polygon {
|
|
|
49
49
|
*/
|
|
50
50
|
addPolygon(dataJSON: MapJSONData, options?: PolygonOptions): VectorLayer<VectorSource>;
|
|
51
51
|
/**
|
|
52
|
-
*
|
|
52
|
+
* 从URL添加多边形图层
|
|
53
53
|
* @param url 数据URL
|
|
54
54
|
* @param options 图层配置选项
|
|
55
55
|
* @returns 创建的矢量图层
|
|
56
56
|
* @throws 当数据格式无效时抛出错误
|
|
57
57
|
*/
|
|
58
|
-
|
|
58
|
+
addPolygonByUrl(url: string, options?: PolygonOptions): VectorLayer<VectorSource>;
|
|
59
59
|
/**
|
|
60
60
|
* 设置要素样式
|
|
61
61
|
* @param features 要素数组
|
package/core/Polygon.js
CHANGED
|
@@ -40,30 +40,50 @@ export default class Polygon {
|
|
|
40
40
|
const key = lev.toString();
|
|
41
41
|
return this.colorMap[key] || 'rgba(128, 128, 128, 0.6)';
|
|
42
42
|
}
|
|
43
|
+
/**
|
|
44
|
+
* 添加地图边框图层
|
|
45
|
+
* @param data 图层数据,必须是有效的 GeoJSON 格式
|
|
46
|
+
* @param options 图层配置选项
|
|
47
|
+
* @returns 创建的图层实例
|
|
48
|
+
* @throws 当数据格式无效时抛出错误
|
|
49
|
+
*/
|
|
43
50
|
addBorderPolygon(data, options) {
|
|
44
|
-
|
|
45
|
-
if (!isUrl) {
|
|
46
|
-
ValidationUtils.validateGeoJSONData(data);
|
|
47
|
-
}
|
|
51
|
+
ValidationUtils.validateGeoJSONData(data);
|
|
48
52
|
const mergedOptions = {
|
|
49
|
-
layerName: 'border',
|
|
50
53
|
fillColor: 'rgba(255, 255, 255, 0)',
|
|
51
54
|
...options
|
|
52
55
|
};
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
? this.addPolygon(data, mergedOptions)
|
|
56
|
-
: this.addPolygon(data, mergedOptions);
|
|
57
|
-
if (mergedOptions.mask && !isUrl) {
|
|
56
|
+
const layer = this.addPolygon(data, mergedOptions);
|
|
57
|
+
if (mergedOptions.mask) {
|
|
58
58
|
this.setOutLayer(data);
|
|
59
59
|
}
|
|
60
60
|
return layer;
|
|
61
61
|
}
|
|
62
|
+
/**
|
|
63
|
+
* 从URL添加地图边框图层
|
|
64
|
+
* @param url 数据URL
|
|
65
|
+
* @param options 图层配置选项
|
|
66
|
+
* @returns 创建的图层实例
|
|
67
|
+
* @throws 当数据格式无效时抛出错误
|
|
68
|
+
*/
|
|
69
|
+
addBorderPolygonByUrl(url, options) {
|
|
70
|
+
const mergedOptions = {
|
|
71
|
+
layerName: 'border',
|
|
72
|
+
fillColor: 'rgba(255, 255, 255, 0)',
|
|
73
|
+
...options
|
|
74
|
+
};
|
|
75
|
+
const layer = this.addPolygonByUrl(url, mergedOptions);
|
|
76
|
+
return layer;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* 添加多边形图层
|
|
80
|
+
* @param dataJSON GeoJSON 数据
|
|
81
|
+
* @param options 图层配置选项
|
|
82
|
+
* @returns 创建的矢量图层
|
|
83
|
+
* @throws 当数据格式无效时抛出错误
|
|
84
|
+
*/
|
|
62
85
|
addPolygon(dataJSON, options) {
|
|
63
|
-
|
|
64
|
-
if (!isUrl) {
|
|
65
|
-
ValidationUtils.validateGeoJSONData(dataJSON);
|
|
66
|
-
}
|
|
86
|
+
ValidationUtils.validateGeoJSONData(dataJSON);
|
|
67
87
|
const mergedOptions = {
|
|
68
88
|
zIndex: 11,
|
|
69
89
|
visible: true,
|
|
@@ -81,48 +101,75 @@ export default class Polygon {
|
|
|
81
101
|
new MapTools(this.map).removeLayer(mergedOptions.layerName);
|
|
82
102
|
}
|
|
83
103
|
let features;
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
: (() => {
|
|
91
|
-
try {
|
|
92
|
-
features = new GeoJSON().readFeatures(dataJSON, mergedOptions.projectionOptOptions ?? {});
|
|
93
|
-
}
|
|
94
|
-
catch (error) {
|
|
95
|
-
throw new Error(`Failed to parse GeoJSON data: ${error}`);
|
|
96
|
-
}
|
|
97
|
-
return new VectorSource({ features });
|
|
98
|
-
})();
|
|
104
|
+
try {
|
|
105
|
+
features = new GeoJSON().readFeatures(dataJSON, mergedOptions.projectionOptOptions ?? {});
|
|
106
|
+
}
|
|
107
|
+
catch (error) {
|
|
108
|
+
throw new Error(`Failed to parse GeoJSON data: ${error}`);
|
|
109
|
+
}
|
|
99
110
|
const layer = new VectorLayer({
|
|
100
111
|
properties: {
|
|
101
112
|
name: mergedOptions.layerName,
|
|
102
113
|
layerName: mergedOptions.layerName
|
|
103
114
|
},
|
|
104
|
-
source,
|
|
115
|
+
source: new VectorSource({ features }),
|
|
105
116
|
zIndex: mergedOptions.zIndex
|
|
106
117
|
});
|
|
107
|
-
//
|
|
108
|
-
|
|
109
|
-
this.setFeatureStyles(features, mergedOptions);
|
|
110
|
-
}
|
|
111
|
-
else {
|
|
112
|
-
// 如果是URL,需要在数据加载后设置样式
|
|
113
|
-
source.once('featuresloadend', () => {
|
|
114
|
-
const loadedFeatures = source.getFeatures();
|
|
115
|
-
this.setFeatureStyles(loadedFeatures, mergedOptions);
|
|
116
|
-
});
|
|
117
|
-
}
|
|
118
|
+
// 设置要素样式
|
|
119
|
+
this.setFeatureStyles(features, mergedOptions);
|
|
118
120
|
layer.setVisible(mergedOptions.visible);
|
|
119
121
|
this.map.addLayer(layer);
|
|
120
122
|
// 如果需要适应视图
|
|
121
|
-
if (mergedOptions.fitView
|
|
123
|
+
if (mergedOptions.fitView) {
|
|
122
124
|
this.fitViewToLayer(layer);
|
|
123
125
|
}
|
|
124
|
-
|
|
125
|
-
|
|
126
|
+
return layer;
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* 从URL添加多边形图层
|
|
130
|
+
* @param url 数据URL
|
|
131
|
+
* @param options 图层配置选项
|
|
132
|
+
* @returns 创建的矢量图层
|
|
133
|
+
* @throws 当数据格式无效时抛出错误
|
|
134
|
+
*/
|
|
135
|
+
addPolygonByUrl(url, options) {
|
|
136
|
+
const mergedOptions = {
|
|
137
|
+
zIndex: 11,
|
|
138
|
+
visible: true,
|
|
139
|
+
strokeColor: '#EBEEF5',
|
|
140
|
+
strokeWidth: 2,
|
|
141
|
+
fillColor: 'rgba(255, 255, 255, 0)',
|
|
142
|
+
textFont: '14px Calibri,sans-serif',
|
|
143
|
+
textFillColor: '#FFF',
|
|
144
|
+
textStrokeColor: '#409EFF',
|
|
145
|
+
textStrokeWidth: 2,
|
|
146
|
+
...options
|
|
147
|
+
};
|
|
148
|
+
// 如果指定了图层名称,先移除同名图层
|
|
149
|
+
if (mergedOptions.layerName) {
|
|
150
|
+
new MapTools(this.map).removeLayer(mergedOptions.layerName);
|
|
151
|
+
}
|
|
152
|
+
const source = new VectorSource({
|
|
153
|
+
url,
|
|
154
|
+
format: new GeoJSON(mergedOptions.projectionOptOptions ?? {})
|
|
155
|
+
});
|
|
156
|
+
const layer = new VectorLayer({
|
|
157
|
+
properties: {
|
|
158
|
+
name: mergedOptions.layerName,
|
|
159
|
+
layerName: mergedOptions.layerName
|
|
160
|
+
},
|
|
161
|
+
source,
|
|
162
|
+
zIndex: mergedOptions.zIndex
|
|
163
|
+
});
|
|
164
|
+
// 在数据加载后设置样式
|
|
165
|
+
source.once('featuresloadend', () => {
|
|
166
|
+
const loadedFeatures = source.getFeatures();
|
|
167
|
+
this.setFeatureStyles(loadedFeatures, mergedOptions);
|
|
168
|
+
});
|
|
169
|
+
layer.setVisible(mergedOptions.visible);
|
|
170
|
+
this.map.addLayer(layer);
|
|
171
|
+
// 如果需要适应视图
|
|
172
|
+
if (mergedOptions.fitView) {
|
|
126
173
|
source.once('featuresloadend', () => {
|
|
127
174
|
this.fitViewToLayer(layer);
|
|
128
175
|
});
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import Map from "ol/Map";
|
|
2
|
+
import { FeatureLike } from "ol/Feature";
|
|
3
|
+
import { Style } from "ol/style";
|
|
4
|
+
import { SelectOptions, SelectMode } from "../types";
|
|
5
|
+
/**
|
|
6
|
+
* 要素选择处理器类
|
|
7
|
+
* 用于在地图上选择和高亮显示要素,支持单选、多选等多种选择模式
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```typescript
|
|
11
|
+
* const selectHandler = new SelectHandler(map);
|
|
12
|
+
*
|
|
13
|
+
* // 启用点击选择
|
|
14
|
+
* selectHandler.enableSelect('click', {
|
|
15
|
+
* layerFilter: ['pointLayer', 'polygonLayer'],
|
|
16
|
+
* multi: false,
|
|
17
|
+
* onSelect: (event) => {
|
|
18
|
+
* console.log('选中要素:', event.selected);
|
|
19
|
+
* }
|
|
20
|
+
* });
|
|
21
|
+
*
|
|
22
|
+
* // 获取当前选中的要素
|
|
23
|
+
* const selected = selectHandler.getSelectedFeatures();
|
|
24
|
+
*
|
|
25
|
+
* // 清除选择
|
|
26
|
+
* selectHandler.clearSelection();
|
|
27
|
+
*
|
|
28
|
+
* // 禁用选择
|
|
29
|
+
* selectHandler.disableSelect();
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
export default class SelectHandler {
|
|
33
|
+
/** OpenLayers 地图实例 */
|
|
34
|
+
private readonly map;
|
|
35
|
+
/** 事件管理器实例 */
|
|
36
|
+
private readonly eventManager;
|
|
37
|
+
/** 错误处理器实例 */
|
|
38
|
+
private readonly errorHandler;
|
|
39
|
+
/** Select 交互实例 */
|
|
40
|
+
private selectInteraction?;
|
|
41
|
+
/** 当前选择模式 */
|
|
42
|
+
private currentMode?;
|
|
43
|
+
/** 当前配置选项 */
|
|
44
|
+
private currentOptions?;
|
|
45
|
+
/** 是否已启用选择 */
|
|
46
|
+
private isEnabled;
|
|
47
|
+
/** 默认选中样式 - 点要素 */
|
|
48
|
+
private readonly defaultPointStyle;
|
|
49
|
+
/** 默认选中样式 - 线要素 */
|
|
50
|
+
private readonly defaultLineStyle;
|
|
51
|
+
/** 默认选中样式 - 面要素 */
|
|
52
|
+
private readonly defaultPolygonStyle;
|
|
53
|
+
/**
|
|
54
|
+
* 构造函数
|
|
55
|
+
* @param map OpenLayers地图实例
|
|
56
|
+
*/
|
|
57
|
+
constructor(map: Map);
|
|
58
|
+
/**
|
|
59
|
+
* 启用要素选择
|
|
60
|
+
* @param mode 选择模式:'click'(点击)、'hover'(悬停)、'box'(框选)、'ctrl'(Ctrl+点击)
|
|
61
|
+
* @param options 选择配置选项
|
|
62
|
+
* @returns SelectHandler 实例(支持链式调用)
|
|
63
|
+
*/
|
|
64
|
+
enableSelect(mode?: SelectMode, options?: SelectOptions): this;
|
|
65
|
+
/**
|
|
66
|
+
* 禁用要素选择
|
|
67
|
+
* @returns SelectHandler 实例(支持链式调用)
|
|
68
|
+
*/
|
|
69
|
+
disableSelect(): this;
|
|
70
|
+
/**
|
|
71
|
+
* 获取当前选中的要素
|
|
72
|
+
* @returns 选中的要素数组
|
|
73
|
+
*/
|
|
74
|
+
getSelectedFeatures(): FeatureLike[];
|
|
75
|
+
/**
|
|
76
|
+
* 清除所有选择
|
|
77
|
+
* @returns SelectHandler 实例(支持链式调用)
|
|
78
|
+
*/
|
|
79
|
+
clearSelection(): this;
|
|
80
|
+
/**
|
|
81
|
+
* 通过要素ID选择要素
|
|
82
|
+
* @param featureIds 要素ID数组
|
|
83
|
+
* @param layerName 图层名称(可选)
|
|
84
|
+
* @param selectStyle 选中样式(可选,仅作用于此次选择)
|
|
85
|
+
* @returns SelectHandler 实例(支持链式调用)
|
|
86
|
+
*/
|
|
87
|
+
selectByIds(featureIds: string[], layerName?: string, selectStyle?: Style | Style[] | ((feature: FeatureLike) => Style | Style[])): this;
|
|
88
|
+
/**
|
|
89
|
+
* 通过属性选择要素
|
|
90
|
+
* @param propertyName 属性名称
|
|
91
|
+
* @param propertyValue 属性值
|
|
92
|
+
* @param layerName 图层名称(可选)
|
|
93
|
+
* @param selectStyle 选中样式(可选,仅作用于此次选择)
|
|
94
|
+
* @returns SelectHandler 实例(支持链式调用)
|
|
95
|
+
*/
|
|
96
|
+
selectByProperty(propertyName: string, propertyValue: any, layerName?: string, selectStyle?: Style | Style[] | ((feature: FeatureLike) => Style | Style[])): this;
|
|
97
|
+
/**
|
|
98
|
+
* 判断选择是否已启用
|
|
99
|
+
* @returns 是否已启用
|
|
100
|
+
*/
|
|
101
|
+
isSelectEnabled(): boolean;
|
|
102
|
+
/**
|
|
103
|
+
* 获取当前选择模式
|
|
104
|
+
* @returns 当前选择模式
|
|
105
|
+
*/
|
|
106
|
+
getCurrentMode(): SelectMode | undefined;
|
|
107
|
+
/**
|
|
108
|
+
* 销毁选择处理器,清理所有资源
|
|
109
|
+
*/
|
|
110
|
+
destroy(): void;
|
|
111
|
+
/**
|
|
112
|
+
* 合并选项配置
|
|
113
|
+
* @private
|
|
114
|
+
*/
|
|
115
|
+
private mergeOptions;
|
|
116
|
+
/**
|
|
117
|
+
* 创建 Select 交互
|
|
118
|
+
* @private
|
|
119
|
+
*/
|
|
120
|
+
private createSelectInteraction;
|
|
121
|
+
/**
|
|
122
|
+
* 获取选择条件
|
|
123
|
+
* @private
|
|
124
|
+
*/
|
|
125
|
+
private getSelectCondition;
|
|
126
|
+
/**
|
|
127
|
+
* 创建图层过滤器
|
|
128
|
+
* @private
|
|
129
|
+
*/
|
|
130
|
+
private createLayerFilter;
|
|
131
|
+
/**
|
|
132
|
+
* 创建选择样式
|
|
133
|
+
* @private
|
|
134
|
+
*/
|
|
135
|
+
private createSelectStyle;
|
|
136
|
+
/**
|
|
137
|
+
* 更新选择样式
|
|
138
|
+
* @param selectStyle 新的选择样式
|
|
139
|
+
* @returns SelectHandler 实例(支持链式调用)
|
|
140
|
+
*/
|
|
141
|
+
updateSelectStyle(selectStyle: Style | Style[] | ((feature: FeatureLike) => Style | Style[])): this;
|
|
142
|
+
/**
|
|
143
|
+
* 附加事件监听器
|
|
144
|
+
* @private
|
|
145
|
+
*/
|
|
146
|
+
private attachEventListeners;
|
|
147
|
+
}
|
|
@@ -0,0 +1,445 @@
|
|
|
1
|
+
import { Select } from "ol/interaction";
|
|
2
|
+
import { click, pointerMove, platformModifierKeyOnly } from "ol/events/condition";
|
|
3
|
+
import VectorLayer from "ol/layer/Vector";
|
|
4
|
+
import { Style, Fill, Stroke, Circle as CircleStyle } from "ol/style";
|
|
5
|
+
import { EventManager } from "./EventManager";
|
|
6
|
+
import { ValidationUtils } from "../utils/ValidationUtils";
|
|
7
|
+
import { ErrorHandler, MyOpenLayersError, ErrorType } from "../utils/ErrorHandler";
|
|
8
|
+
/**
|
|
9
|
+
* 要素选择处理器类
|
|
10
|
+
* 用于在地图上选择和高亮显示要素,支持单选、多选等多种选择模式
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```typescript
|
|
14
|
+
* const selectHandler = new SelectHandler(map);
|
|
15
|
+
*
|
|
16
|
+
* // 启用点击选择
|
|
17
|
+
* selectHandler.enableSelect('click', {
|
|
18
|
+
* layerFilter: ['pointLayer', 'polygonLayer'],
|
|
19
|
+
* multi: false,
|
|
20
|
+
* onSelect: (event) => {
|
|
21
|
+
* console.log('选中要素:', event.selected);
|
|
22
|
+
* }
|
|
23
|
+
* });
|
|
24
|
+
*
|
|
25
|
+
* // 获取当前选中的要素
|
|
26
|
+
* const selected = selectHandler.getSelectedFeatures();
|
|
27
|
+
*
|
|
28
|
+
* // 清除选择
|
|
29
|
+
* selectHandler.clearSelection();
|
|
30
|
+
*
|
|
31
|
+
* // 禁用选择
|
|
32
|
+
* selectHandler.disableSelect();
|
|
33
|
+
* ```
|
|
34
|
+
*/
|
|
35
|
+
export default class SelectHandler {
|
|
36
|
+
/**
|
|
37
|
+
* 构造函数
|
|
38
|
+
* @param map OpenLayers地图实例
|
|
39
|
+
*/
|
|
40
|
+
constructor(map) {
|
|
41
|
+
/** 是否已启用选择 */
|
|
42
|
+
this.isEnabled = false;
|
|
43
|
+
/** 默认选中样式 - 点要素 */
|
|
44
|
+
this.defaultPointStyle = new Style({
|
|
45
|
+
image: new CircleStyle({
|
|
46
|
+
radius: 7,
|
|
47
|
+
fill: new Fill({ color: 'rgba(255, 0, 0, 0.6)' }),
|
|
48
|
+
stroke: new Stroke({ color: '#ff0000', width: 2 })
|
|
49
|
+
})
|
|
50
|
+
});
|
|
51
|
+
/** 默认选中样式 - 线要素 */
|
|
52
|
+
this.defaultLineStyle = new Style({
|
|
53
|
+
stroke: new Stroke({
|
|
54
|
+
color: '#ff0000',
|
|
55
|
+
width: 3
|
|
56
|
+
})
|
|
57
|
+
});
|
|
58
|
+
/** 默认选中样式 - 面要素 */
|
|
59
|
+
this.defaultPolygonStyle = new Style({
|
|
60
|
+
stroke: new Stroke({
|
|
61
|
+
color: '#ff0000',
|
|
62
|
+
width: 2
|
|
63
|
+
}),
|
|
64
|
+
fill: new Fill({
|
|
65
|
+
color: 'rgba(255, 0, 0, 0.2)'
|
|
66
|
+
})
|
|
67
|
+
});
|
|
68
|
+
ValidationUtils.validateMapInstance(map);
|
|
69
|
+
this.map = map;
|
|
70
|
+
this.eventManager = new EventManager(map);
|
|
71
|
+
this.errorHandler = ErrorHandler.getInstance();
|
|
72
|
+
// 默认启用点击选择模式,支持多选
|
|
73
|
+
this.enableSelect('click', { multi: true });
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* 启用要素选择
|
|
77
|
+
* @param mode 选择模式:'click'(点击)、'hover'(悬停)、'box'(框选)、'ctrl'(Ctrl+点击)
|
|
78
|
+
* @param options 选择配置选项
|
|
79
|
+
* @returns SelectHandler 实例(支持链式调用)
|
|
80
|
+
*/
|
|
81
|
+
enableSelect(mode = 'click', options) {
|
|
82
|
+
try {
|
|
83
|
+
// 如果已启用,先禁用
|
|
84
|
+
if (this.isEnabled) {
|
|
85
|
+
this.disableSelect();
|
|
86
|
+
}
|
|
87
|
+
const mergedOptions = this.mergeOptions(options);
|
|
88
|
+
this.currentMode = mode;
|
|
89
|
+
this.currentOptions = mergedOptions;
|
|
90
|
+
// 创建 Select 交互
|
|
91
|
+
this.selectInteraction = this.createSelectInteraction(mode, mergedOptions);
|
|
92
|
+
// 添加事件监听器
|
|
93
|
+
this.attachEventListeners(mergedOptions);
|
|
94
|
+
// 添加到地图
|
|
95
|
+
this.map.addInteraction(this.selectInteraction);
|
|
96
|
+
this.isEnabled = true;
|
|
97
|
+
console.debug('要素选择已启用', { mode, options: mergedOptions });
|
|
98
|
+
return this;
|
|
99
|
+
}
|
|
100
|
+
catch (error) {
|
|
101
|
+
this.errorHandler.handleError(new MyOpenLayersError(`启用要素选择失败: ${error instanceof Error ? error.message : '未知错误'}`, ErrorType.COMPONENT_ERROR, { mode, options }));
|
|
102
|
+
throw error;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* 禁用要素选择
|
|
107
|
+
* @returns SelectHandler 实例(支持链式调用)
|
|
108
|
+
*/
|
|
109
|
+
disableSelect() {
|
|
110
|
+
try {
|
|
111
|
+
if (this.selectInteraction) {
|
|
112
|
+
this.map.removeInteraction(this.selectInteraction);
|
|
113
|
+
this.selectInteraction = undefined;
|
|
114
|
+
}
|
|
115
|
+
this.isEnabled = false;
|
|
116
|
+
this.currentMode = undefined;
|
|
117
|
+
console.debug('要素选择已禁用');
|
|
118
|
+
return this;
|
|
119
|
+
}
|
|
120
|
+
catch (error) {
|
|
121
|
+
this.errorHandler.handleError(new MyOpenLayersError(`禁用要素选择失败: ${error instanceof Error ? error.message : '未知错误'}`, ErrorType.COMPONENT_ERROR));
|
|
122
|
+
throw error;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* 获取当前选中的要素
|
|
127
|
+
* @returns 选中的要素数组
|
|
128
|
+
*/
|
|
129
|
+
getSelectedFeatures() {
|
|
130
|
+
if (!this.selectInteraction) {
|
|
131
|
+
return [];
|
|
132
|
+
}
|
|
133
|
+
const features = this.selectInteraction.getFeatures();
|
|
134
|
+
return features.getArray();
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* 清除所有选择
|
|
138
|
+
* @returns SelectHandler 实例(支持链式调用)
|
|
139
|
+
*/
|
|
140
|
+
clearSelection() {
|
|
141
|
+
if (this.selectInteraction) {
|
|
142
|
+
this.selectInteraction.getFeatures().clear();
|
|
143
|
+
}
|
|
144
|
+
return this;
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* 通过要素ID选择要素
|
|
148
|
+
* @param featureIds 要素ID数组
|
|
149
|
+
* @param layerName 图层名称(可选)
|
|
150
|
+
* @param selectStyle 选中样式(可选,仅作用于此次选择)
|
|
151
|
+
* @returns SelectHandler 实例(支持链式调用)
|
|
152
|
+
*/
|
|
153
|
+
selectByIds(featureIds, layerName, selectStyle) {
|
|
154
|
+
try {
|
|
155
|
+
if (!this.selectInteraction) {
|
|
156
|
+
console.warn('选择交互未启用,无法选择要素');
|
|
157
|
+
return this;
|
|
158
|
+
}
|
|
159
|
+
if (!featureIds || featureIds.length === 0) {
|
|
160
|
+
console.warn('要素ID列表为空');
|
|
161
|
+
return this;
|
|
162
|
+
}
|
|
163
|
+
// 清除当前选择
|
|
164
|
+
this.clearSelection();
|
|
165
|
+
// 获取所有图层
|
|
166
|
+
const layers = this.map.getLayers().getArray();
|
|
167
|
+
for (const layer of layers) {
|
|
168
|
+
// 过滤图层
|
|
169
|
+
if (layerName && layer.get('layerName') !== layerName) {
|
|
170
|
+
continue;
|
|
171
|
+
}
|
|
172
|
+
if (layer instanceof VectorLayer) {
|
|
173
|
+
const source = layer.getSource();
|
|
174
|
+
if (!source)
|
|
175
|
+
continue;
|
|
176
|
+
// 查找并选择要素
|
|
177
|
+
for (const featureId of featureIds) {
|
|
178
|
+
const feature = source.getFeatureById(featureId);
|
|
179
|
+
if (feature) {
|
|
180
|
+
// 如果传入了自定义样式,仅为该要素设置样式
|
|
181
|
+
if (selectStyle) {
|
|
182
|
+
if (typeof selectStyle === 'function') {
|
|
183
|
+
feature.setStyle(selectStyle(feature));
|
|
184
|
+
}
|
|
185
|
+
else {
|
|
186
|
+
feature.setStyle(selectStyle);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
this.selectInteraction.getFeatures().push(feature);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
return this;
|
|
195
|
+
}
|
|
196
|
+
catch (error) {
|
|
197
|
+
this.errorHandler.handleError(new MyOpenLayersError(`通过ID选择要素失败: ${error instanceof Error ? error.message : '未知错误'}`, ErrorType.COMPONENT_ERROR, { featureIds, layerName }));
|
|
198
|
+
throw error;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* 通过属性选择要素
|
|
203
|
+
* @param propertyName 属性名称
|
|
204
|
+
* @param propertyValue 属性值
|
|
205
|
+
* @param layerName 图层名称(可选)
|
|
206
|
+
* @param selectStyle 选中样式(可选,仅作用于此次选择)
|
|
207
|
+
* @returns SelectHandler 实例(支持链式调用)
|
|
208
|
+
*/
|
|
209
|
+
selectByProperty(propertyName, propertyValue, layerName, selectStyle) {
|
|
210
|
+
try {
|
|
211
|
+
if (!this.selectInteraction) {
|
|
212
|
+
console.warn('选择交互未启用,无法选择要素');
|
|
213
|
+
return this;
|
|
214
|
+
}
|
|
215
|
+
if (!propertyName) {
|
|
216
|
+
throw new Error('属性名称不能为空');
|
|
217
|
+
}
|
|
218
|
+
// 清除当前选择
|
|
219
|
+
this.clearSelection();
|
|
220
|
+
// 获取所有图层
|
|
221
|
+
const layers = this.map.getLayers().getArray();
|
|
222
|
+
for (const layer of layers) {
|
|
223
|
+
// 过滤图层
|
|
224
|
+
if (layerName && layer.get('layerName') !== layerName) {
|
|
225
|
+
continue;
|
|
226
|
+
}
|
|
227
|
+
if (layer instanceof VectorLayer) {
|
|
228
|
+
const source = layer.getSource();
|
|
229
|
+
if (!source)
|
|
230
|
+
continue;
|
|
231
|
+
// 查找并选择要素
|
|
232
|
+
const features = source.getFeatures();
|
|
233
|
+
for (const feature of features) {
|
|
234
|
+
if (feature.get(propertyName) === propertyValue) {
|
|
235
|
+
// 如果传入了自定义样式,仅为该要素设置样式
|
|
236
|
+
if (selectStyle) {
|
|
237
|
+
if (typeof selectStyle === 'function') {
|
|
238
|
+
feature.setStyle(selectStyle(feature));
|
|
239
|
+
}
|
|
240
|
+
else {
|
|
241
|
+
feature.setStyle(selectStyle);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
this.selectInteraction.getFeatures().push(feature);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
return this;
|
|
250
|
+
}
|
|
251
|
+
catch (error) {
|
|
252
|
+
this.errorHandler.handleError(new MyOpenLayersError(`通过属性选择要素失败: ${error instanceof Error ? error.message : '未知错误'}`, ErrorType.COMPONENT_ERROR, { propertyName, propertyValue, layerName }));
|
|
253
|
+
throw error;
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
/**
|
|
257
|
+
* 判断选择是否已启用
|
|
258
|
+
* @returns 是否已启用
|
|
259
|
+
*/
|
|
260
|
+
isSelectEnabled() {
|
|
261
|
+
return this.isEnabled;
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* 获取当前选择模式
|
|
265
|
+
* @returns 当前选择模式
|
|
266
|
+
*/
|
|
267
|
+
getCurrentMode() {
|
|
268
|
+
return this.currentMode;
|
|
269
|
+
}
|
|
270
|
+
/**
|
|
271
|
+
* 销毁选择处理器,清理所有资源
|
|
272
|
+
*/
|
|
273
|
+
destroy() {
|
|
274
|
+
try {
|
|
275
|
+
this.disableSelect();
|
|
276
|
+
console.debug('选择处理器已销毁');
|
|
277
|
+
}
|
|
278
|
+
catch (error) {
|
|
279
|
+
this.errorHandler.handleError(new MyOpenLayersError(`销毁选择处理器失败: ${error instanceof Error ? error.message : '未知错误'}`, ErrorType.COMPONENT_ERROR));
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
/**
|
|
283
|
+
* 合并选项配置
|
|
284
|
+
* @private
|
|
285
|
+
*/
|
|
286
|
+
mergeOptions(options) {
|
|
287
|
+
return {
|
|
288
|
+
multi: false,
|
|
289
|
+
layerFilter: undefined,
|
|
290
|
+
featureFilter: undefined,
|
|
291
|
+
hitTolerance: 0,
|
|
292
|
+
selectStyle: undefined,
|
|
293
|
+
onSelect: undefined,
|
|
294
|
+
onDeselect: undefined,
|
|
295
|
+
...options
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
/**
|
|
299
|
+
* 创建 Select 交互
|
|
300
|
+
* @private
|
|
301
|
+
*/
|
|
302
|
+
createSelectInteraction(mode, options) {
|
|
303
|
+
// 确定选择条件
|
|
304
|
+
const condition = this.getSelectCondition(mode);
|
|
305
|
+
// 创建图层过滤器
|
|
306
|
+
const layerFilter = this.createLayerFilter(options.layerFilter);
|
|
307
|
+
// 创建要素过滤器
|
|
308
|
+
const filter = options.featureFilter;
|
|
309
|
+
// 创建选择样式
|
|
310
|
+
const style = this.createSelectStyle(options.selectStyle);
|
|
311
|
+
return new Select({
|
|
312
|
+
condition,
|
|
313
|
+
layers: layerFilter,
|
|
314
|
+
filter,
|
|
315
|
+
style,
|
|
316
|
+
multi: options.multi,
|
|
317
|
+
hitTolerance: options.hitTolerance
|
|
318
|
+
});
|
|
319
|
+
}
|
|
320
|
+
/**
|
|
321
|
+
* 获取选择条件
|
|
322
|
+
* @private
|
|
323
|
+
*/
|
|
324
|
+
getSelectCondition(mode) {
|
|
325
|
+
switch (mode) {
|
|
326
|
+
case 'click':
|
|
327
|
+
return click;
|
|
328
|
+
case 'hover':
|
|
329
|
+
return pointerMove;
|
|
330
|
+
case 'ctrl':
|
|
331
|
+
return platformModifierKeyOnly;
|
|
332
|
+
case 'box':
|
|
333
|
+
// 框选需要额外的 DragBox 交互,这里暂不实现
|
|
334
|
+
return click;
|
|
335
|
+
default:
|
|
336
|
+
return click;
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
/**
|
|
340
|
+
* 创建图层过滤器
|
|
341
|
+
* @private
|
|
342
|
+
*/
|
|
343
|
+
createLayerFilter(layerNames) {
|
|
344
|
+
if (!layerNames || layerNames.length === 0) {
|
|
345
|
+
return undefined;
|
|
346
|
+
}
|
|
347
|
+
return (layer) => {
|
|
348
|
+
const layerName = layer.get('layerName') || layer.get('name');
|
|
349
|
+
return layerNames.includes(layerName);
|
|
350
|
+
};
|
|
351
|
+
}
|
|
352
|
+
/**
|
|
353
|
+
* 创建选择样式
|
|
354
|
+
* @private
|
|
355
|
+
*/
|
|
356
|
+
createSelectStyle(customStyle) {
|
|
357
|
+
if (customStyle) {
|
|
358
|
+
return customStyle;
|
|
359
|
+
}
|
|
360
|
+
// 返回根据几何类型的默认样式
|
|
361
|
+
return (feature) => {
|
|
362
|
+
const geometry = feature.getGeometry();
|
|
363
|
+
if (!geometry) {
|
|
364
|
+
return this.defaultPointStyle;
|
|
365
|
+
}
|
|
366
|
+
const geometryType = geometry.getType();
|
|
367
|
+
switch (geometryType) {
|
|
368
|
+
case 'Point':
|
|
369
|
+
case 'MultiPoint':
|
|
370
|
+
return this.defaultPointStyle;
|
|
371
|
+
case 'LineString':
|
|
372
|
+
case 'MultiLineString':
|
|
373
|
+
return this.defaultLineStyle;
|
|
374
|
+
case 'Polygon':
|
|
375
|
+
case 'MultiPolygon':
|
|
376
|
+
return this.defaultPolygonStyle;
|
|
377
|
+
default:
|
|
378
|
+
return this.defaultPointStyle;
|
|
379
|
+
}
|
|
380
|
+
};
|
|
381
|
+
}
|
|
382
|
+
/**
|
|
383
|
+
* 更新选择样式
|
|
384
|
+
* @param selectStyle 新的选择样式
|
|
385
|
+
* @returns SelectHandler 实例(支持链式调用)
|
|
386
|
+
*/
|
|
387
|
+
updateSelectStyle(selectStyle) {
|
|
388
|
+
if (!this.selectInteraction) {
|
|
389
|
+
console.warn('选择交互未启用,无法更新样式');
|
|
390
|
+
return this;
|
|
391
|
+
}
|
|
392
|
+
try {
|
|
393
|
+
// 更新选择交互的样式
|
|
394
|
+
this.selectInteraction.getStyle = () => {
|
|
395
|
+
if (typeof selectStyle === 'function') {
|
|
396
|
+
return selectStyle;
|
|
397
|
+
}
|
|
398
|
+
return selectStyle;
|
|
399
|
+
};
|
|
400
|
+
// 触发样式更新
|
|
401
|
+
const features = this.selectInteraction.getFeatures();
|
|
402
|
+
features.changed();
|
|
403
|
+
return this;
|
|
404
|
+
}
|
|
405
|
+
catch (error) {
|
|
406
|
+
this.errorHandler.handleError(new MyOpenLayersError(`更新选择样式失败: ${error instanceof Error ? error.message : '未知错误'}`, ErrorType.COMPONENT_ERROR, { selectStyle }));
|
|
407
|
+
throw error;
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
/**
|
|
411
|
+
* 附加事件监听器
|
|
412
|
+
* @private
|
|
413
|
+
*/
|
|
414
|
+
attachEventListeners(options) {
|
|
415
|
+
if (!this.selectInteraction) {
|
|
416
|
+
return;
|
|
417
|
+
}
|
|
418
|
+
// 监听选择事件
|
|
419
|
+
this.selectInteraction.on('select', (event) => {
|
|
420
|
+
const callbackEvent = {
|
|
421
|
+
selected: event.selected,
|
|
422
|
+
deselected: event.deselected,
|
|
423
|
+
mapBrowserEvent: event.mapBrowserEvent
|
|
424
|
+
};
|
|
425
|
+
// 触发选择回调
|
|
426
|
+
if (options.onSelect && event.selected.length > 0) {
|
|
427
|
+
try {
|
|
428
|
+
options.onSelect(callbackEvent);
|
|
429
|
+
}
|
|
430
|
+
catch (error) {
|
|
431
|
+
console.error('选择回调执行失败:', error);
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
// 触发取消选择回调
|
|
435
|
+
if (options.onDeselect && event.deselected.length > 0) {
|
|
436
|
+
try {
|
|
437
|
+
options.onDeselect(callbackEvent);
|
|
438
|
+
}
|
|
439
|
+
catch (error) {
|
|
440
|
+
console.error('取消选择回调执行失败:', error);
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
});
|
|
444
|
+
}
|
|
445
|
+
}
|
package/index.d.ts
CHANGED
|
@@ -7,6 +7,7 @@ export { default as MapBaseLayers } from './core/MapBaseLayers';
|
|
|
7
7
|
export { default as MapTools } from './core/MapTools';
|
|
8
8
|
export { default as MeasureHandler } from './core/MeasureHandler';
|
|
9
9
|
export { default as VueTemplatePoint } from './core/VueTemplatePoint';
|
|
10
|
+
export { default as SelectHandler } from './core/SelectHandler';
|
|
10
11
|
export { ConfigManager } from './core/ConfigManager';
|
|
11
12
|
export { EventManager } from './core/EventManager';
|
|
12
13
|
export type { MapEventType, EventCallback, MapEventData } from './core/EventManager';
|
|
@@ -15,4 +16,4 @@ export { ValidationUtils } from './utils/ValidationUtils';
|
|
|
15
16
|
export type { BaseOptions, StyleOptions, TextOptions } from './types';
|
|
16
17
|
export type { PointOptions, LineOptions, PolygonOptions } from './types';
|
|
17
18
|
export type { OptionsType } from './types';
|
|
18
|
-
export type { MapInitType, MapLayersOptions, HeatMapOptions, ImageLayerData, MaskLayerOptions, ColorMap, FeatureColorUpdateOptions, PointData, LineData, ClusterOptions, MeasureHandlerType, VueTemplatePointOptions, MapJSONData, FeatureData, AnnotationType, TiandituType, MapLayers, AnnotationLayerOptions } from './types';
|
|
19
|
+
export type { MapInitType, MapLayersOptions, HeatMapOptions, ImageLayerData, MaskLayerOptions, ColorMap, FeatureColorUpdateOptions, PointData, LineData, ClusterOptions, MeasureHandlerType, VueTemplatePointOptions, MapJSONData, FeatureData, AnnotationType, TiandituType, MapLayers, AnnotationLayerOptions, SelectOptions, SelectMode, SelectCallbackEvent } from './types';
|
package/index.js
CHANGED
|
@@ -8,6 +8,7 @@ export { default as MapBaseLayers } from './core/MapBaseLayers';
|
|
|
8
8
|
export { default as MapTools } from './core/MapTools';
|
|
9
9
|
export { default as MeasureHandler } from './core/MeasureHandler';
|
|
10
10
|
export { default as VueTemplatePoint } from './core/VueTemplatePoint';
|
|
11
|
+
export { default as SelectHandler } from './core/SelectHandler';
|
|
11
12
|
// 新增工具类
|
|
12
13
|
export { ConfigManager } from './core/ConfigManager';
|
|
13
14
|
export { EventManager } from './core/EventManager';
|
package/package.json
CHANGED
package/types.d.ts
CHANGED
|
@@ -4,6 +4,7 @@ import { WMTS } from "ol/source";
|
|
|
4
4
|
import View from "ol/View";
|
|
5
5
|
import Feature, { FeatureLike } from "ol/Feature";
|
|
6
6
|
import { Style } from "ol/style";
|
|
7
|
+
import MapBrowserEvent from "ol/MapBrowserEvent";
|
|
7
8
|
export interface FeatureData {
|
|
8
9
|
type: string;
|
|
9
10
|
properties: any;
|
|
@@ -302,4 +303,38 @@ export interface VueTemplatePointInstance {
|
|
|
302
303
|
getOptions(): Readonly<VueTemplatePointOptions>;
|
|
303
304
|
isDestroyed(): boolean;
|
|
304
305
|
}
|
|
306
|
+
/**
|
|
307
|
+
* 选择模式类型
|
|
308
|
+
*/
|
|
309
|
+
export type SelectMode = 'click' | 'hover' | 'box' | 'ctrl';
|
|
310
|
+
/**
|
|
311
|
+
* 选择回调事件接口
|
|
312
|
+
*/
|
|
313
|
+
export interface SelectCallbackEvent {
|
|
314
|
+
/** 新选中的要素数组 */
|
|
315
|
+
selected: FeatureLike[];
|
|
316
|
+
/** 取消选中的要素数组 */
|
|
317
|
+
deselected: FeatureLike[];
|
|
318
|
+
/** 地图浏览器事件 */
|
|
319
|
+
mapBrowserEvent: MapBrowserEvent<any>;
|
|
320
|
+
}
|
|
321
|
+
/**
|
|
322
|
+
* 要素选择配置选项
|
|
323
|
+
*/
|
|
324
|
+
export interface SelectOptions {
|
|
325
|
+
/** 是否支持多选,默认 false */
|
|
326
|
+
multi?: boolean;
|
|
327
|
+
/** 图层过滤器,指定可选择的图层名称列表 */
|
|
328
|
+
layerFilter?: string[];
|
|
329
|
+
/** 要素过滤器函数 */
|
|
330
|
+
featureFilter?: (feature: FeatureLike) => boolean;
|
|
331
|
+
/** 点击容差(像素),默认为 0 */
|
|
332
|
+
hitTolerance?: number;
|
|
333
|
+
/** 选中要素的样式 */
|
|
334
|
+
selectStyle?: Style | Style[] | ((feature: FeatureLike) => Style | Style[]);
|
|
335
|
+
/** 选中要素时的回调函数 */
|
|
336
|
+
onSelect?: (event: SelectCallbackEvent) => void;
|
|
337
|
+
/** 取消选中要素时的回调函数 */
|
|
338
|
+
onDeselect?: (event: SelectCallbackEvent) => void;
|
|
339
|
+
}
|
|
305
340
|
export {};
|