my-openlayer 2.0.0 → 2.1.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/MyOl.d.ts +135 -128
- package/MyOl.js +401 -381
- package/README.md +0 -21
- package/core/ConfigManager.d.ts +88 -88
- package/core/ConfigManager.js +112 -112
- package/core/EventManager.d.ts +141 -141
- package/core/EventManager.js +316 -316
- package/core/Line.d.ts +130 -109
- package/core/Line.js +512 -288
- package/core/MapBaseLayers.d.ts +234 -234
- package/core/MapBaseLayers.js +573 -573
- package/core/MapTools.d.ts +68 -68
- package/core/MapTools.js +202 -201
- package/core/MeasureHandler.d.ts +65 -65
- package/core/MeasureHandler.js +312 -312
- package/core/Point.d.ts +94 -94
- package/core/Point.js +348 -348
- package/core/Polygon.d.ts +157 -139
- package/core/Polygon.js +605 -529
- package/core/SelectHandler.d.ts +138 -0
- package/core/SelectHandler.js +395 -0
- package/core/VueTemplatePoint.d.ts +51 -51
- package/core/VueTemplatePoint.js +529 -529
- package/index.d.ts +19 -18
- package/index.js +18 -17
- package/package.json +1 -1
- package/types.d.ts +340 -302
- package/types.js +11 -11
- package/utils/ErrorHandler.d.ts +102 -102
- package/utils/ErrorHandler.js +191 -191
- package/utils/ValidationUtils.d.ts +163 -163
- package/utils/ValidationUtils.js +312 -312
package/core/MapBaseLayers.js
CHANGED
|
@@ -1,573 +1,573 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 地图底图图层管理类
|
|
3
|
-
* 提供天地图底图、注记图层、GeoServer图层等功能
|
|
4
|
-
* 支持图层切换、裁剪等高级功能
|
|
5
|
-
*/
|
|
6
|
-
import { Tile as TileLayer } from "ol/layer";
|
|
7
|
-
import { get as getProjection } from "ol/proj";
|
|
8
|
-
import { getTopLeft, getWidth } from "ol/extent";
|
|
9
|
-
import { TileWMS } from "ol/source";
|
|
10
|
-
import WMTSTileGrid from "ol/tilegrid/WMTS";
|
|
11
|
-
import XYZ from "ol/source/XYZ";
|
|
12
|
-
import MapTools from "./MapTools";
|
|
13
|
-
import { ErrorHandler, ErrorType } from "../utils/ErrorHandler";
|
|
14
|
-
import { ValidationUtils } from "../utils/ValidationUtils";
|
|
15
|
-
/**
|
|
16
|
-
* 天地图服务器配置
|
|
17
|
-
*/
|
|
18
|
-
const TIANDITU_CONFIG = {
|
|
19
|
-
BASE_URL: '//t{0-7}.tianditu.gov.cn/DataServer',
|
|
20
|
-
PROJECTION: 'EPSG:4326',
|
|
21
|
-
DEFAULT_ZINDEX: 9,
|
|
22
|
-
ANNOTATION_ZINDEX_OFFSET: 10
|
|
23
|
-
};
|
|
24
|
-
const TIANDITU_TYPES = ['vec_c', 'img_c', 'ter_c'];
|
|
25
|
-
/**
|
|
26
|
-
* 地图底图图层管理类
|
|
27
|
-
*/
|
|
28
|
-
export default class MapBaseLayers {
|
|
29
|
-
/**
|
|
30
|
-
* 构造函数
|
|
31
|
-
* @param map OpenLayers地图实例
|
|
32
|
-
* @param options 图层配置选项
|
|
33
|
-
*/
|
|
34
|
-
constructor(map, options) {
|
|
35
|
-
this.layers = {};
|
|
36
|
-
this.currentBaseLayerType = null;
|
|
37
|
-
this.currentAnnotationLayer = null;
|
|
38
|
-
this.currentAnnotationType = null;
|
|
39
|
-
this.errorHandler = ErrorHandler.getInstance();
|
|
40
|
-
try {
|
|
41
|
-
// 参数验证
|
|
42
|
-
this.validateConstructorParams(map, options);
|
|
43
|
-
this.map = map;
|
|
44
|
-
this.options = this.mergeDefaultOptions(options);
|
|
45
|
-
// 初始化图层
|
|
46
|
-
this.initializeLayers();
|
|
47
|
-
if (this.layers && Object.keys(this.layers).length > 0) {
|
|
48
|
-
this.addMapLayer();
|
|
49
|
-
const firstLayerType = Object.keys(this.layers)[0];
|
|
50
|
-
this.switchBaseLayer(firstLayerType);
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
catch (error) {
|
|
54
|
-
this.errorHandler.createAndHandleError(`Failed to initialize MapBaseLayers: ${error}`, ErrorType.MAP_ERROR, { map, options, error });
|
|
55
|
-
throw error;
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
/**
|
|
59
|
-
* 验证构造函数参数
|
|
60
|
-
* @param map 地图实例
|
|
61
|
-
* @param options 配置选项
|
|
62
|
-
* @private
|
|
63
|
-
*/
|
|
64
|
-
validateConstructorParams(map, options) {
|
|
65
|
-
ValidationUtils.validateMap(map);
|
|
66
|
-
ValidationUtils.validateOptions(options);
|
|
67
|
-
}
|
|
68
|
-
/**
|
|
69
|
-
* 合并默认配置选项
|
|
70
|
-
* @param options 用户配置选项
|
|
71
|
-
* @returns 合并后的配置选项
|
|
72
|
-
* @private
|
|
73
|
-
*/
|
|
74
|
-
mergeDefaultOptions(options) {
|
|
75
|
-
const defaultOptions = {
|
|
76
|
-
zIndex: TIANDITU_CONFIG.DEFAULT_ZINDEX,
|
|
77
|
-
annotation: false,
|
|
78
|
-
mapClip: false,
|
|
79
|
-
mapClipData: undefined,
|
|
80
|
-
};
|
|
81
|
-
return { ...defaultOptions, ...options };
|
|
82
|
-
}
|
|
83
|
-
/**
|
|
84
|
-
* 初始化图层
|
|
85
|
-
* @private
|
|
86
|
-
*/
|
|
87
|
-
initializeLayers() {
|
|
88
|
-
// 如果没有配置底图,则默认使用天地图底图
|
|
89
|
-
if (!Array.isArray(this.options.layers)) {
|
|
90
|
-
this.layers = this.options.layers || {};
|
|
91
|
-
if (this.options.token) {
|
|
92
|
-
this.initTiandituLayers();
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
// 添加注记图层
|
|
96
|
-
if (this.options.annotation) {
|
|
97
|
-
if (!this.options.token) {
|
|
98
|
-
throw new Error('请配置token后才能使用天地图注记');
|
|
99
|
-
}
|
|
100
|
-
const { token, zIndex = TIANDITU_CONFIG.DEFAULT_ZINDEX } = this.options;
|
|
101
|
-
this.loadDefaultAnnotationLayer(token, zIndex);
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
/**
|
|
105
|
-
* 初始化天地图图层
|
|
106
|
-
* @private
|
|
107
|
-
*/
|
|
108
|
-
initTiandituLayers() {
|
|
109
|
-
if (!this.options.token) {
|
|
110
|
-
throw new Error('Token is required for Tianditu layers');
|
|
111
|
-
}
|
|
112
|
-
const { token, zIndex = TIANDITU_CONFIG.DEFAULT_ZINDEX } = this.options;
|
|
113
|
-
try {
|
|
114
|
-
// 创建基础图层
|
|
115
|
-
this.layers.vec_c = [this.createTiandituLayer({ type: 'vec_c', token, zIndex, visible: false })];
|
|
116
|
-
this.layers.img_c = [this.createTiandituLayer({ type: 'img_c', token, zIndex, visible: false })];
|
|
117
|
-
this.layers.ter_c = [this.createTiandituLayer({ type: 'ter_c', token, zIndex, visible: false })];
|
|
118
|
-
}
|
|
119
|
-
catch (error) {
|
|
120
|
-
this.errorHandler.createAndHandleError(`Failed to initialize Tianditu layers: ${error}`, ErrorType.LAYER_ERROR, { token, zIndex, error });
|
|
121
|
-
throw error;
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
/**
|
|
125
|
-
* 加载默认注记图层(cia_c)
|
|
126
|
-
* @param token 天地图token
|
|
127
|
-
* @param baseZIndex 基础层级
|
|
128
|
-
* @private
|
|
129
|
-
*/
|
|
130
|
-
loadDefaultAnnotationLayer(token, baseZIndex) {
|
|
131
|
-
this.setAnnotationLayer('cia_c', token, baseZIndex);
|
|
132
|
-
}
|
|
133
|
-
/**
|
|
134
|
-
* 切换注记类别
|
|
135
|
-
* @param annotationType 注记类型 ('cva_c' | 'cia_c' | 'cta_c')
|
|
136
|
-
*/
|
|
137
|
-
switchAnnotationLayer(annotationType) {
|
|
138
|
-
try {
|
|
139
|
-
if (!this.options.token) {
|
|
140
|
-
throw new Error('Token is required for annotation layer');
|
|
141
|
-
}
|
|
142
|
-
if (!this.options.annotation) {
|
|
143
|
-
throw new Error('Annotation is not enabled in options');
|
|
144
|
-
}
|
|
145
|
-
const baseZIndex = this.options.zIndex ?? TIANDITU_CONFIG.DEFAULT_ZINDEX;
|
|
146
|
-
this.setAnnotationLayer(annotationType, this.options.token, baseZIndex);
|
|
147
|
-
}
|
|
148
|
-
catch (error) {
|
|
149
|
-
this.errorHandler.createAndHandleError(`Failed to switch annotation layer to '${annotationType}': ${error}`, ErrorType.LAYER_ERROR, { annotationType, error });
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
/**
|
|
153
|
-
* 设置注记图层(私有方法,用于消除代码重复)
|
|
154
|
-
* @param annotationType 注记类型
|
|
155
|
-
* @param token 天地图token
|
|
156
|
-
* @param baseZIndex 基础层级
|
|
157
|
-
* @private
|
|
158
|
-
*/
|
|
159
|
-
setAnnotationLayer(annotationType, token, baseZIndex) {
|
|
160
|
-
// 移除当前注记图层
|
|
161
|
-
if (this.currentAnnotationLayer) {
|
|
162
|
-
this.map.removeLayer(this.currentAnnotationLayer);
|
|
163
|
-
}
|
|
164
|
-
// 创建新的注记图层,确保层级在基本图层之上
|
|
165
|
-
const annotationZIndex = baseZIndex + TIANDITU_CONFIG.ANNOTATION_ZINDEX_OFFSET;
|
|
166
|
-
let annotationLayer = this.createAnnotationLayer({
|
|
167
|
-
type: annotationType,
|
|
168
|
-
token,
|
|
169
|
-
zIndex: annotationZIndex,
|
|
170
|
-
visible: true
|
|
171
|
-
});
|
|
172
|
-
// 应用剪切处理
|
|
173
|
-
annotationLayer = this.processLayer(annotationLayer);
|
|
174
|
-
this.currentAnnotationLayer = annotationLayer;
|
|
175
|
-
this.currentAnnotationType = annotationType;
|
|
176
|
-
this.map.addLayer(this.currentAnnotationLayer);
|
|
177
|
-
}
|
|
178
|
-
/**
|
|
179
|
-
* 获取当前注记类型
|
|
180
|
-
* @returns 当前注记类型
|
|
181
|
-
*/
|
|
182
|
-
getCurrentAnnotationType() {
|
|
183
|
-
return this.currentAnnotationType;
|
|
184
|
-
}
|
|
185
|
-
/**
|
|
186
|
-
* 显示/隐藏注记图层
|
|
187
|
-
* @param visible 是否可见
|
|
188
|
-
*/
|
|
189
|
-
setAnnotationVisible(visible) {
|
|
190
|
-
if (this.currentAnnotationLayer) {
|
|
191
|
-
this.currentAnnotationLayer.setVisible(visible);
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
/**
|
|
195
|
-
* 检查注记图层是否可见
|
|
196
|
-
* @returns 是否可见
|
|
197
|
-
*/
|
|
198
|
-
isAnnotationVisible() {
|
|
199
|
-
return this.currentAnnotationLayer ? this.currentAnnotationLayer.getVisible() : false;
|
|
200
|
-
}
|
|
201
|
-
/**
|
|
202
|
-
* 切换底图图层
|
|
203
|
-
* @param type 图层类型
|
|
204
|
-
*/
|
|
205
|
-
switchBaseLayer(type) {
|
|
206
|
-
try {
|
|
207
|
-
if (Array.isArray(this.options.layers)) {
|
|
208
|
-
this.errorHandler.createAndHandleError('需要按照键值对的方式配置底图才可使用切换底图功能', ErrorType.LAYER_ERROR, { layersType: 'array', requestedType: type });
|
|
209
|
-
return;
|
|
210
|
-
}
|
|
211
|
-
if (TIANDITU_TYPES.includes(type) && !this.options.token) {
|
|
212
|
-
this.errorHandler.createAndHandleError('请配置token后才能使用天地图底图', ErrorType.LAYER_ERROR, { requestedType: type });
|
|
213
|
-
return;
|
|
214
|
-
}
|
|
215
|
-
if (!this.layers[type]) {
|
|
216
|
-
this.errorHandler.createAndHandleError(`图层类型 '${type}' 不存在`, ErrorType.LAYER_ERROR, { availableTypes: Object.keys(this.layers), requestedType: type });
|
|
217
|
-
return;
|
|
218
|
-
}
|
|
219
|
-
// 隐藏所有图层
|
|
220
|
-
for (const key in this.layers) {
|
|
221
|
-
this.layers[key]?.forEach((layer) => {
|
|
222
|
-
layer.setVisible(false);
|
|
223
|
-
});
|
|
224
|
-
}
|
|
225
|
-
// 显示指定类型的图层
|
|
226
|
-
this.layers[type]?.forEach((layer) => {
|
|
227
|
-
layer.setVisible(true);
|
|
228
|
-
});
|
|
229
|
-
this.currentBaseLayerType = type;
|
|
230
|
-
// 如果存在注记图层,更新其层级确保在新的基本图层之上
|
|
231
|
-
if (this.currentAnnotationLayer && this.currentAnnotationType) {
|
|
232
|
-
const baseZIndex = this.options.zIndex ?? TIANDITU_CONFIG.DEFAULT_ZINDEX;
|
|
233
|
-
const annotationZIndex = baseZIndex + TIANDITU_CONFIG.ANNOTATION_ZINDEX_OFFSET;
|
|
234
|
-
this.currentAnnotationLayer.setZIndex(annotationZIndex);
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
catch (error) {
|
|
238
|
-
this.errorHandler.createAndHandleError(`Failed to switch base layer to '${type}': ${error}`, ErrorType.LAYER_ERROR, { type, error });
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
/**
|
|
242
|
-
* 获取当前底图类型
|
|
243
|
-
* @returns 当前底图类型
|
|
244
|
-
*/
|
|
245
|
-
getCurrentBaseLayerType() {
|
|
246
|
-
return this.currentBaseLayerType;
|
|
247
|
-
}
|
|
248
|
-
/**
|
|
249
|
-
* 获取可用的图层类型列表
|
|
250
|
-
* @returns 图层类型数组
|
|
251
|
-
*/
|
|
252
|
-
getAvailableLayerTypes() {
|
|
253
|
-
return Object.keys(this.layers);
|
|
254
|
-
}
|
|
255
|
-
/**
|
|
256
|
-
* 检查指定图层类型是否存在
|
|
257
|
-
* @param type 图层类型
|
|
258
|
-
* @returns 是否存在
|
|
259
|
-
*/
|
|
260
|
-
hasLayerType(type) {
|
|
261
|
-
return type in this.layers && this.layers[type] !== undefined;
|
|
262
|
-
}
|
|
263
|
-
/**
|
|
264
|
-
* 添加注记图层(实例方法)
|
|
265
|
-
* @param options 注记图层选项(不包含token)
|
|
266
|
-
* @returns 创建的图层
|
|
267
|
-
*/
|
|
268
|
-
addAnnotationLayer(options) {
|
|
269
|
-
try {
|
|
270
|
-
if (!this.options.token) {
|
|
271
|
-
throw new Error('Token is required for annotation layer');
|
|
272
|
-
}
|
|
273
|
-
return MapBaseLayers.addAnnotationLayer(this.map, {
|
|
274
|
-
...options,
|
|
275
|
-
token: this.options.token
|
|
276
|
-
});
|
|
277
|
-
}
|
|
278
|
-
catch (error) {
|
|
279
|
-
this.errorHandler.createAndHandleError(`Failed to add annotation layer: ${error}`, ErrorType.LAYER_ERROR, { options, error });
|
|
280
|
-
throw error;
|
|
281
|
-
}
|
|
282
|
-
}
|
|
283
|
-
/**
|
|
284
|
-
* 添加注记图层(静态方法)
|
|
285
|
-
* @param map 地图实例
|
|
286
|
-
* @param options 注记图层选项
|
|
287
|
-
* @returns 创建的图层
|
|
288
|
-
*/
|
|
289
|
-
static addAnnotationLayer(map, options) {
|
|
290
|
-
try {
|
|
291
|
-
ErrorHandler.validateMap(map);
|
|
292
|
-
if (!options.token) {
|
|
293
|
-
throw new Error('Token is required for annotation layer');
|
|
294
|
-
}
|
|
295
|
-
const layer = MapBaseLayers.createAnnotationLayer({
|
|
296
|
-
type: options.type,
|
|
297
|
-
token: options.token,
|
|
298
|
-
zIndex: options.zIndex ?? TIANDITU_CONFIG.DEFAULT_ZINDEX,
|
|
299
|
-
visible: options.visible ?? true
|
|
300
|
-
});
|
|
301
|
-
map.addLayer(layer);
|
|
302
|
-
return layer;
|
|
303
|
-
}
|
|
304
|
-
catch (error) {
|
|
305
|
-
const errorHandler = ErrorHandler.getInstance();
|
|
306
|
-
errorHandler.createAndHandleError(`Failed to add annotation layer: ${error}`, ErrorType.LAYER_ERROR, { options, error });
|
|
307
|
-
throw error;
|
|
308
|
-
}
|
|
309
|
-
}
|
|
310
|
-
/**
|
|
311
|
-
* 将所有图层添加到地图
|
|
312
|
-
* @private
|
|
313
|
-
*/
|
|
314
|
-
addMapLayer() {
|
|
315
|
-
try {
|
|
316
|
-
if (!this.layers || Object.keys(this.layers).length === 0) {
|
|
317
|
-
return;
|
|
318
|
-
}
|
|
319
|
-
for (const key in this.layers) {
|
|
320
|
-
this.layers[key]?.forEach((layer) => {
|
|
321
|
-
const processedLayer = this.processLayer(layer);
|
|
322
|
-
this.map.addLayer(processedLayer);
|
|
323
|
-
});
|
|
324
|
-
}
|
|
325
|
-
}
|
|
326
|
-
catch (error) {
|
|
327
|
-
this.errorHandler.createAndHandleError(`Failed to add map layers: ${error}`, ErrorType.LAYER_ERROR, { layersCount: Object.keys(this.layers).length, error });
|
|
328
|
-
throw error;
|
|
329
|
-
}
|
|
330
|
-
}
|
|
331
|
-
/**
|
|
332
|
-
* 处理图层(应用裁剪等)
|
|
333
|
-
* @param layer 原始图层
|
|
334
|
-
* @returns 处理后的图层
|
|
335
|
-
* @private
|
|
336
|
-
*/
|
|
337
|
-
processLayer(layer) {
|
|
338
|
-
try {
|
|
339
|
-
let processedLayer = layer;
|
|
340
|
-
if (this.options.mapClip && this.options.mapClipData) {
|
|
341
|
-
processedLayer = MapTools.setMapClip(layer, this.options.mapClipData);
|
|
342
|
-
}
|
|
343
|
-
return processedLayer;
|
|
344
|
-
}
|
|
345
|
-
catch (error) {
|
|
346
|
-
this.errorHandler.createAndHandleError(`Failed to process layer: ${error}`, ErrorType.LAYER_ERROR, { hasMapClip: !!this.options.mapClip, hasMapClipData: !!this.options.mapClipData, error });
|
|
347
|
-
throw error;
|
|
348
|
-
}
|
|
349
|
-
}
|
|
350
|
-
/**
|
|
351
|
-
* 添加GeoServer图层
|
|
352
|
-
* @param url GeoServer服务URL
|
|
353
|
-
* @param layerName 图层名称
|
|
354
|
-
* @param options 图层选项
|
|
355
|
-
* @returns 创建的WMS图层
|
|
356
|
-
*/
|
|
357
|
-
addGeoServerLayer(url, layerName, options = {}) {
|
|
358
|
-
try {
|
|
359
|
-
ValidationUtils.validateNonEmptyString(url, 'Valid URL is required for GeoServer layer');
|
|
360
|
-
ValidationUtils.validateNonEmptyString(layerName, 'Valid layer name is required for GeoServer layer');
|
|
361
|
-
const wmsLayer = new TileLayer({
|
|
362
|
-
source: new TileWMS({
|
|
363
|
-
url: url,
|
|
364
|
-
params: {
|
|
365
|
-
'LAYERS': layerName,
|
|
366
|
-
'TILED': true,
|
|
367
|
-
'VERSION': options.version || '1.1.1',
|
|
368
|
-
...options.params
|
|
369
|
-
},
|
|
370
|
-
serverType: 'geoserver',
|
|
371
|
-
crossOrigin: options.crossOrigin || 'anonymous',
|
|
372
|
-
}),
|
|
373
|
-
zIndex: options.zIndex ?? TIANDITU_CONFIG.DEFAULT_ZINDEX,
|
|
374
|
-
visible: options.visible ?? true,
|
|
375
|
-
});
|
|
376
|
-
this.map.addLayer(wmsLayer);
|
|
377
|
-
return wmsLayer;
|
|
378
|
-
}
|
|
379
|
-
catch (error) {
|
|
380
|
-
this.errorHandler.createAndHandleError(`Failed to add GeoServer layer: ${error}`, ErrorType.LAYER_ERROR, { url, layerName, options, error });
|
|
381
|
-
throw error;
|
|
382
|
-
}
|
|
383
|
-
}
|
|
384
|
-
/**
|
|
385
|
-
* 创建天地图图层(实例方法)
|
|
386
|
-
* @param options 天地图图层选项
|
|
387
|
-
* @returns 创建的图层
|
|
388
|
-
* @private
|
|
389
|
-
*/
|
|
390
|
-
createTiandituLayer(options) {
|
|
391
|
-
return MapBaseLayers.getTiandiTuLayer(options);
|
|
392
|
-
}
|
|
393
|
-
/**
|
|
394
|
-
* 创建注记图层(实例方法)
|
|
395
|
-
* @param options 注记图层选项
|
|
396
|
-
* @returns 创建的图层
|
|
397
|
-
* @private
|
|
398
|
-
*/
|
|
399
|
-
createAnnotationLayer(options) {
|
|
400
|
-
return MapBaseLayers.createAnnotationLayer(options);
|
|
401
|
-
}
|
|
402
|
-
/**
|
|
403
|
-
* 创建天地图底图图层(静态方法)
|
|
404
|
-
* @param options 天地图图层选项
|
|
405
|
-
* @returns 创建的图层
|
|
406
|
-
*/
|
|
407
|
-
static getTiandiTuLayer(options) {
|
|
408
|
-
try {
|
|
409
|
-
if (!options.token) {
|
|
410
|
-
throw new Error('Token is required for Tianditu layer');
|
|
411
|
-
}
|
|
412
|
-
if (!options.type) {
|
|
413
|
-
throw new Error('Layer type is required for Tianditu layer');
|
|
414
|
-
}
|
|
415
|
-
return new TileLayer({
|
|
416
|
-
source: new XYZ({
|
|
417
|
-
url: `//t{0-7}.tianditu.gov.cn/DataServer?T=${options.type}&tk=${options.token}&x={x}&y={y}&l={z}`,
|
|
418
|
-
projection: 'EPSG:4326'
|
|
419
|
-
}),
|
|
420
|
-
zIndex: options.zIndex ?? TIANDITU_CONFIG.DEFAULT_ZINDEX,
|
|
421
|
-
visible: options.visible ?? false
|
|
422
|
-
});
|
|
423
|
-
}
|
|
424
|
-
catch (error) {
|
|
425
|
-
const errorHandler = ErrorHandler.getInstance();
|
|
426
|
-
errorHandler.createAndHandleError(`Failed to create Tianditu layer: ${error}`, ErrorType.LAYER_ERROR, { options, error });
|
|
427
|
-
throw error;
|
|
428
|
-
}
|
|
429
|
-
}
|
|
430
|
-
/**
|
|
431
|
-
* 创建天地图注记图层(静态方法)
|
|
432
|
-
* @param options 注记图层选项
|
|
433
|
-
* @returns 创建的图层
|
|
434
|
-
*/
|
|
435
|
-
static createAnnotationLayer(options) {
|
|
436
|
-
try {
|
|
437
|
-
if (!options.token) {
|
|
438
|
-
throw new Error('Token is required for annotation layer');
|
|
439
|
-
}
|
|
440
|
-
if (!options.type) {
|
|
441
|
-
throw new Error('Annotation type is required for annotation layer');
|
|
442
|
-
}
|
|
443
|
-
return new TileLayer({
|
|
444
|
-
source: new XYZ({
|
|
445
|
-
url: `//t{0-7}.tianditu.gov.cn/DataServer?T=${options.type}&tk=${options.token}&x={x}&y={y}&l={z}`,
|
|
446
|
-
projection: 'EPSG:4326'
|
|
447
|
-
}),
|
|
448
|
-
zIndex: options.zIndex ?? TIANDITU_CONFIG.DEFAULT_ZINDEX,
|
|
449
|
-
visible: options.visible ?? false
|
|
450
|
-
});
|
|
451
|
-
}
|
|
452
|
-
catch (error) {
|
|
453
|
-
const errorHandler = ErrorHandler.getInstance();
|
|
454
|
-
errorHandler.createAndHandleError(`Failed to create annotation layer: ${error}`, ErrorType.LAYER_ERROR, { options, error });
|
|
455
|
-
throw error;
|
|
456
|
-
}
|
|
457
|
-
}
|
|
458
|
-
/**
|
|
459
|
-
* 获取天地图注记图层(向后兼容的静态方法)
|
|
460
|
-
* @param options 注记图层选项
|
|
461
|
-
* @returns 创建的图层
|
|
462
|
-
* @deprecated 使用 createAnnotationLayer 替代
|
|
463
|
-
*/
|
|
464
|
-
static getAnnotationLayer(options) {
|
|
465
|
-
return MapBaseLayers.createAnnotationLayer(options);
|
|
466
|
-
}
|
|
467
|
-
/**
|
|
468
|
-
* 创建WMTS瓦片网格
|
|
469
|
-
* @param length 层级数量
|
|
470
|
-
* @returns WMTS瓦片网格
|
|
471
|
-
*/
|
|
472
|
-
static getTileGrid(length) {
|
|
473
|
-
try {
|
|
474
|
-
ValidationUtils.validatePositiveNumber(length, 'Valid length is required for tile grid');
|
|
475
|
-
const projection = getProjection('EPSG:4326');
|
|
476
|
-
if (!projection) {
|
|
477
|
-
throw new Error('Failed to get EPSG:4326 projection');
|
|
478
|
-
}
|
|
479
|
-
const projectionExtent = projection.getExtent();
|
|
480
|
-
const size = getWidth(projectionExtent) / 256;
|
|
481
|
-
const resolutions = new Array(length);
|
|
482
|
-
const matrixIds = new Array(length);
|
|
483
|
-
for (let i = 0; i < length; i += 1) {
|
|
484
|
-
const pow = Math.pow(2, i);
|
|
485
|
-
resolutions[i] = size / pow;
|
|
486
|
-
matrixIds[i] = i;
|
|
487
|
-
}
|
|
488
|
-
return new WMTSTileGrid({
|
|
489
|
-
origin: getTopLeft(projectionExtent),
|
|
490
|
-
resolutions,
|
|
491
|
-
matrixIds
|
|
492
|
-
});
|
|
493
|
-
}
|
|
494
|
-
catch (error) {
|
|
495
|
-
const errorHandler = ErrorHandler.getInstance();
|
|
496
|
-
errorHandler.createAndHandleError(`Failed to create tile grid: ${error}`, ErrorType.MAP_ERROR, { length, error });
|
|
497
|
-
throw error;
|
|
498
|
-
}
|
|
499
|
-
}
|
|
500
|
-
/**
|
|
501
|
-
* 移除指定类型的图层
|
|
502
|
-
* @param type 图层类型
|
|
503
|
-
*/
|
|
504
|
-
removeLayersByType(type) {
|
|
505
|
-
try {
|
|
506
|
-
if (!this.layers[type]) {
|
|
507
|
-
return;
|
|
508
|
-
}
|
|
509
|
-
this.layers[type]
|
|
510
|
-
this.map.removeLayer(layer);
|
|
511
|
-
});
|
|
512
|
-
delete this.layers[type];
|
|
513
|
-
if (this.currentBaseLayerType === type) {
|
|
514
|
-
this.currentBaseLayerType = null;
|
|
515
|
-
}
|
|
516
|
-
}
|
|
517
|
-
catch (error) {
|
|
518
|
-
this.errorHandler.createAndHandleError(`Failed to remove layers of type '${type}': ${error}`, ErrorType.LAYER_ERROR, { type, error });
|
|
519
|
-
}
|
|
520
|
-
}
|
|
521
|
-
/**
|
|
522
|
-
* 清除所有图层
|
|
523
|
-
*/
|
|
524
|
-
clearAllLayers() {
|
|
525
|
-
try {
|
|
526
|
-
for (const key in this.layers) {
|
|
527
|
-
this.layers[key]?.forEach((layer) => {
|
|
528
|
-
this.map.removeLayer(layer);
|
|
529
|
-
});
|
|
530
|
-
}
|
|
531
|
-
// 清除注记图层
|
|
532
|
-
if (this.currentAnnotationLayer) {
|
|
533
|
-
this.map.removeLayer(this.currentAnnotationLayer);
|
|
534
|
-
this.currentAnnotationLayer = null;
|
|
535
|
-
this.currentAnnotationType = null;
|
|
536
|
-
}
|
|
537
|
-
this.layers = {};
|
|
538
|
-
this.currentBaseLayerType = null;
|
|
539
|
-
}
|
|
540
|
-
catch (error) {
|
|
541
|
-
this.errorHandler.createAndHandleError(`Failed to clear all layers: ${error}`, ErrorType.LAYER_ERROR, { error });
|
|
542
|
-
}
|
|
543
|
-
}
|
|
544
|
-
/**
|
|
545
|
-
* 获取图层数量统计
|
|
546
|
-
* @returns 图层统计信息
|
|
547
|
-
*/
|
|
548
|
-
getLayerStats() {
|
|
549
|
-
const layersByType = {};
|
|
550
|
-
let totalLayers = 0;
|
|
551
|
-
for (const key in this.layers) {
|
|
552
|
-
const count = this.layers[key]?.length || 0;
|
|
553
|
-
layersByType[key] = count;
|
|
554
|
-
totalLayers += count;
|
|
555
|
-
}
|
|
556
|
-
return {
|
|
557
|
-
totalTypes: Object.keys(this.layers).length,
|
|
558
|
-
totalLayers,
|
|
559
|
-
layersByType
|
|
560
|
-
};
|
|
561
|
-
}
|
|
562
|
-
/**
|
|
563
|
-
* 销毁实例,清理资源
|
|
564
|
-
*/
|
|
565
|
-
destroy() {
|
|
566
|
-
try {
|
|
567
|
-
this.clearAllLayers();
|
|
568
|
-
}
|
|
569
|
-
catch (error) {
|
|
570
|
-
this.errorHandler.createAndHandleError(`Failed to destroy MapBaseLayers: ${error}`, ErrorType.MAP_ERROR, { error });
|
|
571
|
-
}
|
|
572
|
-
}
|
|
573
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* 地图底图图层管理类
|
|
3
|
+
* 提供天地图底图、注记图层、GeoServer图层等功能
|
|
4
|
+
* 支持图层切换、裁剪等高级功能
|
|
5
|
+
*/
|
|
6
|
+
import { Tile as TileLayer } from "ol/layer";
|
|
7
|
+
import { get as getProjection } from "ol/proj";
|
|
8
|
+
import { getTopLeft, getWidth } from "ol/extent";
|
|
9
|
+
import { TileWMS } from "ol/source";
|
|
10
|
+
import WMTSTileGrid from "ol/tilegrid/WMTS";
|
|
11
|
+
import XYZ from "ol/source/XYZ";
|
|
12
|
+
import MapTools from "./MapTools";
|
|
13
|
+
import { ErrorHandler, ErrorType } from "../utils/ErrorHandler";
|
|
14
|
+
import { ValidationUtils } from "../utils/ValidationUtils";
|
|
15
|
+
/**
|
|
16
|
+
* 天地图服务器配置
|
|
17
|
+
*/
|
|
18
|
+
const TIANDITU_CONFIG = {
|
|
19
|
+
BASE_URL: '//t{0-7}.tianditu.gov.cn/DataServer',
|
|
20
|
+
PROJECTION: 'EPSG:4326',
|
|
21
|
+
DEFAULT_ZINDEX: 9,
|
|
22
|
+
ANNOTATION_ZINDEX_OFFSET: 10
|
|
23
|
+
};
|
|
24
|
+
const TIANDITU_TYPES = ['vec_c', 'img_c', 'ter_c'];
|
|
25
|
+
/**
|
|
26
|
+
* 地图底图图层管理类
|
|
27
|
+
*/
|
|
28
|
+
export default class MapBaseLayers {
|
|
29
|
+
/**
|
|
30
|
+
* 构造函数
|
|
31
|
+
* @param map OpenLayers地图实例
|
|
32
|
+
* @param options 图层配置选项
|
|
33
|
+
*/
|
|
34
|
+
constructor(map, options) {
|
|
35
|
+
this.layers = {};
|
|
36
|
+
this.currentBaseLayerType = null;
|
|
37
|
+
this.currentAnnotationLayer = null;
|
|
38
|
+
this.currentAnnotationType = null;
|
|
39
|
+
this.errorHandler = ErrorHandler.getInstance();
|
|
40
|
+
try {
|
|
41
|
+
// 参数验证
|
|
42
|
+
this.validateConstructorParams(map, options);
|
|
43
|
+
this.map = map;
|
|
44
|
+
this.options = this.mergeDefaultOptions(options);
|
|
45
|
+
// 初始化图层
|
|
46
|
+
this.initializeLayers();
|
|
47
|
+
if (this.layers && Object.keys(this.layers).length > 0) {
|
|
48
|
+
this.addMapLayer();
|
|
49
|
+
const firstLayerType = Object.keys(this.layers)[0];
|
|
50
|
+
this.switchBaseLayer(firstLayerType);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
catch (error) {
|
|
54
|
+
this.errorHandler.createAndHandleError(`Failed to initialize MapBaseLayers: ${error}`, ErrorType.MAP_ERROR, { map, options, error });
|
|
55
|
+
throw error;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* 验证构造函数参数
|
|
60
|
+
* @param map 地图实例
|
|
61
|
+
* @param options 配置选项
|
|
62
|
+
* @private
|
|
63
|
+
*/
|
|
64
|
+
validateConstructorParams(map, options) {
|
|
65
|
+
ValidationUtils.validateMap(map);
|
|
66
|
+
ValidationUtils.validateOptions(options);
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* 合并默认配置选项
|
|
70
|
+
* @param options 用户配置选项
|
|
71
|
+
* @returns 合并后的配置选项
|
|
72
|
+
* @private
|
|
73
|
+
*/
|
|
74
|
+
mergeDefaultOptions(options) {
|
|
75
|
+
const defaultOptions = {
|
|
76
|
+
zIndex: TIANDITU_CONFIG.DEFAULT_ZINDEX,
|
|
77
|
+
annotation: false,
|
|
78
|
+
mapClip: false,
|
|
79
|
+
mapClipData: undefined,
|
|
80
|
+
};
|
|
81
|
+
return { ...defaultOptions, ...options };
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* 初始化图层
|
|
85
|
+
* @private
|
|
86
|
+
*/
|
|
87
|
+
initializeLayers() {
|
|
88
|
+
// 如果没有配置底图,则默认使用天地图底图
|
|
89
|
+
if (!Array.isArray(this.options.layers)) {
|
|
90
|
+
this.layers = this.options.layers || {};
|
|
91
|
+
if (this.options.token) {
|
|
92
|
+
this.initTiandituLayers();
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
// 添加注记图层
|
|
96
|
+
if (this.options.annotation) {
|
|
97
|
+
if (!this.options.token) {
|
|
98
|
+
throw new Error('请配置token后才能使用天地图注记');
|
|
99
|
+
}
|
|
100
|
+
const { token, zIndex = TIANDITU_CONFIG.DEFAULT_ZINDEX } = this.options;
|
|
101
|
+
this.loadDefaultAnnotationLayer(token, zIndex);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* 初始化天地图图层
|
|
106
|
+
* @private
|
|
107
|
+
*/
|
|
108
|
+
initTiandituLayers() {
|
|
109
|
+
if (!this.options.token) {
|
|
110
|
+
throw new Error('Token is required for Tianditu layers');
|
|
111
|
+
}
|
|
112
|
+
const { token, zIndex = TIANDITU_CONFIG.DEFAULT_ZINDEX } = this.options;
|
|
113
|
+
try {
|
|
114
|
+
// 创建基础图层
|
|
115
|
+
this.layers.vec_c = [this.createTiandituLayer({ type: 'vec_c', token, zIndex, visible: false })];
|
|
116
|
+
this.layers.img_c = [this.createTiandituLayer({ type: 'img_c', token, zIndex, visible: false })];
|
|
117
|
+
this.layers.ter_c = [this.createTiandituLayer({ type: 'ter_c', token, zIndex, visible: false })];
|
|
118
|
+
}
|
|
119
|
+
catch (error) {
|
|
120
|
+
this.errorHandler.createAndHandleError(`Failed to initialize Tianditu layers: ${error}`, ErrorType.LAYER_ERROR, { token, zIndex, error });
|
|
121
|
+
throw error;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* 加载默认注记图层(cia_c)
|
|
126
|
+
* @param token 天地图token
|
|
127
|
+
* @param baseZIndex 基础层级
|
|
128
|
+
* @private
|
|
129
|
+
*/
|
|
130
|
+
loadDefaultAnnotationLayer(token, baseZIndex) {
|
|
131
|
+
this.setAnnotationLayer('cia_c', token, baseZIndex);
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* 切换注记类别
|
|
135
|
+
* @param annotationType 注记类型 ('cva_c' | 'cia_c' | 'cta_c')
|
|
136
|
+
*/
|
|
137
|
+
switchAnnotationLayer(annotationType) {
|
|
138
|
+
try {
|
|
139
|
+
if (!this.options.token) {
|
|
140
|
+
throw new Error('Token is required for annotation layer');
|
|
141
|
+
}
|
|
142
|
+
if (!this.options.annotation) {
|
|
143
|
+
throw new Error('Annotation is not enabled in options');
|
|
144
|
+
}
|
|
145
|
+
const baseZIndex = this.options.zIndex ?? TIANDITU_CONFIG.DEFAULT_ZINDEX;
|
|
146
|
+
this.setAnnotationLayer(annotationType, this.options.token, baseZIndex);
|
|
147
|
+
}
|
|
148
|
+
catch (error) {
|
|
149
|
+
this.errorHandler.createAndHandleError(`Failed to switch annotation layer to '${annotationType}': ${error}`, ErrorType.LAYER_ERROR, { annotationType, error });
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* 设置注记图层(私有方法,用于消除代码重复)
|
|
154
|
+
* @param annotationType 注记类型
|
|
155
|
+
* @param token 天地图token
|
|
156
|
+
* @param baseZIndex 基础层级
|
|
157
|
+
* @private
|
|
158
|
+
*/
|
|
159
|
+
setAnnotationLayer(annotationType, token, baseZIndex) {
|
|
160
|
+
// 移除当前注记图层
|
|
161
|
+
if (this.currentAnnotationLayer) {
|
|
162
|
+
this.map.removeLayer(this.currentAnnotationLayer);
|
|
163
|
+
}
|
|
164
|
+
// 创建新的注记图层,确保层级在基本图层之上
|
|
165
|
+
const annotationZIndex = baseZIndex + TIANDITU_CONFIG.ANNOTATION_ZINDEX_OFFSET;
|
|
166
|
+
let annotationLayer = this.createAnnotationLayer({
|
|
167
|
+
type: annotationType,
|
|
168
|
+
token,
|
|
169
|
+
zIndex: annotationZIndex,
|
|
170
|
+
visible: true
|
|
171
|
+
});
|
|
172
|
+
// 应用剪切处理
|
|
173
|
+
annotationLayer = this.processLayer(annotationLayer);
|
|
174
|
+
this.currentAnnotationLayer = annotationLayer;
|
|
175
|
+
this.currentAnnotationType = annotationType;
|
|
176
|
+
this.map.addLayer(this.currentAnnotationLayer);
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* 获取当前注记类型
|
|
180
|
+
* @returns 当前注记类型
|
|
181
|
+
*/
|
|
182
|
+
getCurrentAnnotationType() {
|
|
183
|
+
return this.currentAnnotationType;
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* 显示/隐藏注记图层
|
|
187
|
+
* @param visible 是否可见
|
|
188
|
+
*/
|
|
189
|
+
setAnnotationVisible(visible) {
|
|
190
|
+
if (this.currentAnnotationLayer) {
|
|
191
|
+
this.currentAnnotationLayer.setVisible(visible);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* 检查注记图层是否可见
|
|
196
|
+
* @returns 是否可见
|
|
197
|
+
*/
|
|
198
|
+
isAnnotationVisible() {
|
|
199
|
+
return this.currentAnnotationLayer ? this.currentAnnotationLayer.getVisible() : false;
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* 切换底图图层
|
|
203
|
+
* @param type 图层类型
|
|
204
|
+
*/
|
|
205
|
+
switchBaseLayer(type) {
|
|
206
|
+
try {
|
|
207
|
+
if (Array.isArray(this.options.layers)) {
|
|
208
|
+
this.errorHandler.createAndHandleError('需要按照键值对的方式配置底图才可使用切换底图功能', ErrorType.LAYER_ERROR, { layersType: 'array', requestedType: type });
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
if (TIANDITU_TYPES.includes(type) && !this.options.token) {
|
|
212
|
+
this.errorHandler.createAndHandleError('请配置token后才能使用天地图底图', ErrorType.LAYER_ERROR, { requestedType: type });
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
if (!this.layers[type]) {
|
|
216
|
+
this.errorHandler.createAndHandleError(`图层类型 '${type}' 不存在`, ErrorType.LAYER_ERROR, { availableTypes: Object.keys(this.layers), requestedType: type });
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
// 隐藏所有图层
|
|
220
|
+
for (const key in this.layers) {
|
|
221
|
+
this.layers[key]?.forEach((layer) => {
|
|
222
|
+
layer.setVisible(false);
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
// 显示指定类型的图层
|
|
226
|
+
this.layers[type]?.forEach((layer) => {
|
|
227
|
+
layer.setVisible(true);
|
|
228
|
+
});
|
|
229
|
+
this.currentBaseLayerType = type;
|
|
230
|
+
// 如果存在注记图层,更新其层级确保在新的基本图层之上
|
|
231
|
+
if (this.currentAnnotationLayer && this.currentAnnotationType) {
|
|
232
|
+
const baseZIndex = this.options.zIndex ?? TIANDITU_CONFIG.DEFAULT_ZINDEX;
|
|
233
|
+
const annotationZIndex = baseZIndex + TIANDITU_CONFIG.ANNOTATION_ZINDEX_OFFSET;
|
|
234
|
+
this.currentAnnotationLayer.setZIndex(annotationZIndex);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
catch (error) {
|
|
238
|
+
this.errorHandler.createAndHandleError(`Failed to switch base layer to '${type}': ${error}`, ErrorType.LAYER_ERROR, { type, error });
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* 获取当前底图类型
|
|
243
|
+
* @returns 当前底图类型
|
|
244
|
+
*/
|
|
245
|
+
getCurrentBaseLayerType() {
|
|
246
|
+
return this.currentBaseLayerType;
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* 获取可用的图层类型列表
|
|
250
|
+
* @returns 图层类型数组
|
|
251
|
+
*/
|
|
252
|
+
getAvailableLayerTypes() {
|
|
253
|
+
return Object.keys(this.layers);
|
|
254
|
+
}
|
|
255
|
+
/**
|
|
256
|
+
* 检查指定图层类型是否存在
|
|
257
|
+
* @param type 图层类型
|
|
258
|
+
* @returns 是否存在
|
|
259
|
+
*/
|
|
260
|
+
hasLayerType(type) {
|
|
261
|
+
return type in this.layers && this.layers[type] !== undefined;
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* 添加注记图层(实例方法)
|
|
265
|
+
* @param options 注记图层选项(不包含token)
|
|
266
|
+
* @returns 创建的图层
|
|
267
|
+
*/
|
|
268
|
+
addAnnotationLayer(options) {
|
|
269
|
+
try {
|
|
270
|
+
if (!this.options.token) {
|
|
271
|
+
throw new Error('Token is required for annotation layer');
|
|
272
|
+
}
|
|
273
|
+
return MapBaseLayers.addAnnotationLayer(this.map, {
|
|
274
|
+
...options,
|
|
275
|
+
token: this.options.token
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
catch (error) {
|
|
279
|
+
this.errorHandler.createAndHandleError(`Failed to add annotation layer: ${error}`, ErrorType.LAYER_ERROR, { options, error });
|
|
280
|
+
throw error;
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
/**
|
|
284
|
+
* 添加注记图层(静态方法)
|
|
285
|
+
* @param map 地图实例
|
|
286
|
+
* @param options 注记图层选项
|
|
287
|
+
* @returns 创建的图层
|
|
288
|
+
*/
|
|
289
|
+
static addAnnotationLayer(map, options) {
|
|
290
|
+
try {
|
|
291
|
+
ErrorHandler.validateMap(map);
|
|
292
|
+
if (!options.token) {
|
|
293
|
+
throw new Error('Token is required for annotation layer');
|
|
294
|
+
}
|
|
295
|
+
const layer = MapBaseLayers.createAnnotationLayer({
|
|
296
|
+
type: options.type,
|
|
297
|
+
token: options.token,
|
|
298
|
+
zIndex: options.zIndex ?? TIANDITU_CONFIG.DEFAULT_ZINDEX,
|
|
299
|
+
visible: options.visible ?? true
|
|
300
|
+
});
|
|
301
|
+
map.addLayer(layer);
|
|
302
|
+
return layer;
|
|
303
|
+
}
|
|
304
|
+
catch (error) {
|
|
305
|
+
const errorHandler = ErrorHandler.getInstance();
|
|
306
|
+
errorHandler.createAndHandleError(`Failed to add annotation layer: ${error}`, ErrorType.LAYER_ERROR, { options, error });
|
|
307
|
+
throw error;
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
/**
|
|
311
|
+
* 将所有图层添加到地图
|
|
312
|
+
* @private
|
|
313
|
+
*/
|
|
314
|
+
addMapLayer() {
|
|
315
|
+
try {
|
|
316
|
+
if (!this.layers || Object.keys(this.layers).length === 0) {
|
|
317
|
+
return;
|
|
318
|
+
}
|
|
319
|
+
for (const key in this.layers) {
|
|
320
|
+
this.layers[key]?.forEach((layer) => {
|
|
321
|
+
const processedLayer = this.processLayer(layer);
|
|
322
|
+
this.map.addLayer(processedLayer);
|
|
323
|
+
});
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
catch (error) {
|
|
327
|
+
this.errorHandler.createAndHandleError(`Failed to add map layers: ${error}`, ErrorType.LAYER_ERROR, { layersCount: Object.keys(this.layers).length, error });
|
|
328
|
+
throw error;
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
/**
|
|
332
|
+
* 处理图层(应用裁剪等)
|
|
333
|
+
* @param layer 原始图层
|
|
334
|
+
* @returns 处理后的图层
|
|
335
|
+
* @private
|
|
336
|
+
*/
|
|
337
|
+
processLayer(layer) {
|
|
338
|
+
try {
|
|
339
|
+
let processedLayer = layer;
|
|
340
|
+
if (this.options.mapClip && this.options.mapClipData) {
|
|
341
|
+
processedLayer = MapTools.setMapClip(layer, this.options.mapClipData);
|
|
342
|
+
}
|
|
343
|
+
return processedLayer;
|
|
344
|
+
}
|
|
345
|
+
catch (error) {
|
|
346
|
+
this.errorHandler.createAndHandleError(`Failed to process layer: ${error}`, ErrorType.LAYER_ERROR, { hasMapClip: !!this.options.mapClip, hasMapClipData: !!this.options.mapClipData, error });
|
|
347
|
+
throw error;
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
/**
|
|
351
|
+
* 添加GeoServer图层
|
|
352
|
+
* @param url GeoServer服务URL
|
|
353
|
+
* @param layerName 图层名称
|
|
354
|
+
* @param options 图层选项
|
|
355
|
+
* @returns 创建的WMS图层
|
|
356
|
+
*/
|
|
357
|
+
addGeoServerLayer(url, layerName, options = {}) {
|
|
358
|
+
try {
|
|
359
|
+
ValidationUtils.validateNonEmptyString(url, 'Valid URL is required for GeoServer layer');
|
|
360
|
+
ValidationUtils.validateNonEmptyString(layerName, 'Valid layer name is required for GeoServer layer');
|
|
361
|
+
const wmsLayer = new TileLayer({
|
|
362
|
+
source: new TileWMS({
|
|
363
|
+
url: url,
|
|
364
|
+
params: {
|
|
365
|
+
'LAYERS': layerName,
|
|
366
|
+
'TILED': true,
|
|
367
|
+
'VERSION': options.version || '1.1.1',
|
|
368
|
+
...options.params
|
|
369
|
+
},
|
|
370
|
+
serverType: 'geoserver',
|
|
371
|
+
crossOrigin: options.crossOrigin || 'anonymous',
|
|
372
|
+
}),
|
|
373
|
+
zIndex: options.zIndex ?? TIANDITU_CONFIG.DEFAULT_ZINDEX,
|
|
374
|
+
visible: options.visible ?? true,
|
|
375
|
+
});
|
|
376
|
+
this.map.addLayer(wmsLayer);
|
|
377
|
+
return wmsLayer;
|
|
378
|
+
}
|
|
379
|
+
catch (error) {
|
|
380
|
+
this.errorHandler.createAndHandleError(`Failed to add GeoServer layer: ${error}`, ErrorType.LAYER_ERROR, { url, layerName, options, error });
|
|
381
|
+
throw error;
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
/**
|
|
385
|
+
* 创建天地图图层(实例方法)
|
|
386
|
+
* @param options 天地图图层选项
|
|
387
|
+
* @returns 创建的图层
|
|
388
|
+
* @private
|
|
389
|
+
*/
|
|
390
|
+
createTiandituLayer(options) {
|
|
391
|
+
return MapBaseLayers.getTiandiTuLayer(options);
|
|
392
|
+
}
|
|
393
|
+
/**
|
|
394
|
+
* 创建注记图层(实例方法)
|
|
395
|
+
* @param options 注记图层选项
|
|
396
|
+
* @returns 创建的图层
|
|
397
|
+
* @private
|
|
398
|
+
*/
|
|
399
|
+
createAnnotationLayer(options) {
|
|
400
|
+
return MapBaseLayers.createAnnotationLayer(options);
|
|
401
|
+
}
|
|
402
|
+
/**
|
|
403
|
+
* 创建天地图底图图层(静态方法)
|
|
404
|
+
* @param options 天地图图层选项
|
|
405
|
+
* @returns 创建的图层
|
|
406
|
+
*/
|
|
407
|
+
static getTiandiTuLayer(options) {
|
|
408
|
+
try {
|
|
409
|
+
if (!options.token) {
|
|
410
|
+
throw new Error('Token is required for Tianditu layer');
|
|
411
|
+
}
|
|
412
|
+
if (!options.type) {
|
|
413
|
+
throw new Error('Layer type is required for Tianditu layer');
|
|
414
|
+
}
|
|
415
|
+
return new TileLayer({
|
|
416
|
+
source: new XYZ({
|
|
417
|
+
url: `//t{0-7}.tianditu.gov.cn/DataServer?T=${options.type}&tk=${options.token}&x={x}&y={y}&l={z}`,
|
|
418
|
+
projection: 'EPSG:4326'
|
|
419
|
+
}),
|
|
420
|
+
zIndex: options.zIndex ?? TIANDITU_CONFIG.DEFAULT_ZINDEX,
|
|
421
|
+
visible: options.visible ?? false
|
|
422
|
+
});
|
|
423
|
+
}
|
|
424
|
+
catch (error) {
|
|
425
|
+
const errorHandler = ErrorHandler.getInstance();
|
|
426
|
+
errorHandler.createAndHandleError(`Failed to create Tianditu layer: ${error}`, ErrorType.LAYER_ERROR, { options, error });
|
|
427
|
+
throw error;
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
/**
|
|
431
|
+
* 创建天地图注记图层(静态方法)
|
|
432
|
+
* @param options 注记图层选项
|
|
433
|
+
* @returns 创建的图层
|
|
434
|
+
*/
|
|
435
|
+
static createAnnotationLayer(options) {
|
|
436
|
+
try {
|
|
437
|
+
if (!options.token) {
|
|
438
|
+
throw new Error('Token is required for annotation layer');
|
|
439
|
+
}
|
|
440
|
+
if (!options.type) {
|
|
441
|
+
throw new Error('Annotation type is required for annotation layer');
|
|
442
|
+
}
|
|
443
|
+
return new TileLayer({
|
|
444
|
+
source: new XYZ({
|
|
445
|
+
url: `//t{0-7}.tianditu.gov.cn/DataServer?T=${options.type}&tk=${options.token}&x={x}&y={y}&l={z}`,
|
|
446
|
+
projection: 'EPSG:4326'
|
|
447
|
+
}),
|
|
448
|
+
zIndex: options.zIndex ?? TIANDITU_CONFIG.DEFAULT_ZINDEX,
|
|
449
|
+
visible: options.visible ?? false
|
|
450
|
+
});
|
|
451
|
+
}
|
|
452
|
+
catch (error) {
|
|
453
|
+
const errorHandler = ErrorHandler.getInstance();
|
|
454
|
+
errorHandler.createAndHandleError(`Failed to create annotation layer: ${error}`, ErrorType.LAYER_ERROR, { options, error });
|
|
455
|
+
throw error;
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
/**
|
|
459
|
+
* 获取天地图注记图层(向后兼容的静态方法)
|
|
460
|
+
* @param options 注记图层选项
|
|
461
|
+
* @returns 创建的图层
|
|
462
|
+
* @deprecated 使用 createAnnotationLayer 替代
|
|
463
|
+
*/
|
|
464
|
+
static getAnnotationLayer(options) {
|
|
465
|
+
return MapBaseLayers.createAnnotationLayer(options);
|
|
466
|
+
}
|
|
467
|
+
/**
|
|
468
|
+
* 创建WMTS瓦片网格
|
|
469
|
+
* @param length 层级数量
|
|
470
|
+
* @returns WMTS瓦片网格
|
|
471
|
+
*/
|
|
472
|
+
static getTileGrid(length) {
|
|
473
|
+
try {
|
|
474
|
+
ValidationUtils.validatePositiveNumber(length, 'Valid length is required for tile grid');
|
|
475
|
+
const projection = getProjection('EPSG:4326');
|
|
476
|
+
if (!projection) {
|
|
477
|
+
throw new Error('Failed to get EPSG:4326 projection');
|
|
478
|
+
}
|
|
479
|
+
const projectionExtent = projection.getExtent();
|
|
480
|
+
const size = getWidth(projectionExtent) / 256;
|
|
481
|
+
const resolutions = new Array(length);
|
|
482
|
+
const matrixIds = new Array(length);
|
|
483
|
+
for (let i = 0; i < length; i += 1) {
|
|
484
|
+
const pow = Math.pow(2, i);
|
|
485
|
+
resolutions[i] = size / pow;
|
|
486
|
+
matrixIds[i] = i;
|
|
487
|
+
}
|
|
488
|
+
return new WMTSTileGrid({
|
|
489
|
+
origin: getTopLeft(projectionExtent),
|
|
490
|
+
resolutions,
|
|
491
|
+
matrixIds
|
|
492
|
+
});
|
|
493
|
+
}
|
|
494
|
+
catch (error) {
|
|
495
|
+
const errorHandler = ErrorHandler.getInstance();
|
|
496
|
+
errorHandler.createAndHandleError(`Failed to create tile grid: ${error}`, ErrorType.MAP_ERROR, { length, error });
|
|
497
|
+
throw error;
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
/**
|
|
501
|
+
* 移除指定类型的图层
|
|
502
|
+
* @param type 图层类型
|
|
503
|
+
*/
|
|
504
|
+
removeLayersByType(type) {
|
|
505
|
+
try {
|
|
506
|
+
if (!this.layers[type]) {
|
|
507
|
+
return;
|
|
508
|
+
}
|
|
509
|
+
this.layers[type].forEach((layer) => {
|
|
510
|
+
this.map.removeLayer(layer);
|
|
511
|
+
});
|
|
512
|
+
delete this.layers[type];
|
|
513
|
+
if (this.currentBaseLayerType === type) {
|
|
514
|
+
this.currentBaseLayerType = null;
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
catch (error) {
|
|
518
|
+
this.errorHandler.createAndHandleError(`Failed to remove layers of type '${type}': ${error}`, ErrorType.LAYER_ERROR, { type, error });
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
/**
|
|
522
|
+
* 清除所有图层
|
|
523
|
+
*/
|
|
524
|
+
clearAllLayers() {
|
|
525
|
+
try {
|
|
526
|
+
for (const key in this.layers) {
|
|
527
|
+
this.layers[key]?.forEach((layer) => {
|
|
528
|
+
this.map.removeLayer(layer);
|
|
529
|
+
});
|
|
530
|
+
}
|
|
531
|
+
// 清除注记图层
|
|
532
|
+
if (this.currentAnnotationLayer) {
|
|
533
|
+
this.map.removeLayer(this.currentAnnotationLayer);
|
|
534
|
+
this.currentAnnotationLayer = null;
|
|
535
|
+
this.currentAnnotationType = null;
|
|
536
|
+
}
|
|
537
|
+
this.layers = {};
|
|
538
|
+
this.currentBaseLayerType = null;
|
|
539
|
+
}
|
|
540
|
+
catch (error) {
|
|
541
|
+
this.errorHandler.createAndHandleError(`Failed to clear all layers: ${error}`, ErrorType.LAYER_ERROR, { error });
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
/**
|
|
545
|
+
* 获取图层数量统计
|
|
546
|
+
* @returns 图层统计信息
|
|
547
|
+
*/
|
|
548
|
+
getLayerStats() {
|
|
549
|
+
const layersByType = {};
|
|
550
|
+
let totalLayers = 0;
|
|
551
|
+
for (const key in this.layers) {
|
|
552
|
+
const count = this.layers[key]?.length || 0;
|
|
553
|
+
layersByType[key] = count;
|
|
554
|
+
totalLayers += count;
|
|
555
|
+
}
|
|
556
|
+
return {
|
|
557
|
+
totalTypes: Object.keys(this.layers).length,
|
|
558
|
+
totalLayers,
|
|
559
|
+
layersByType
|
|
560
|
+
};
|
|
561
|
+
}
|
|
562
|
+
/**
|
|
563
|
+
* 销毁实例,清理资源
|
|
564
|
+
*/
|
|
565
|
+
destroy() {
|
|
566
|
+
try {
|
|
567
|
+
this.clearAllLayers();
|
|
568
|
+
}
|
|
569
|
+
catch (error) {
|
|
570
|
+
this.errorHandler.createAndHandleError(`Failed to destroy MapBaseLayers: ${error}`, ErrorType.MAP_ERROR, { error });
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
}
|