map-ol-utils 0.2.0 → 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/dist/index.mjs CHANGED
@@ -1,1830 +1 @@
1
- import { a as normalizePrivateGeometry, c as resolveFeatureProperties, i as encodeStandardMvt, l as restoreTdtBytes, n as TDT_XOR_BYTE_INDEXES, o as normalizePrivateMvt, r as decodeMvt, s as privateTypeToStandard, t as loadAndNormalizeTdtVts } from "./tile-loader-CtZwCurI.mjs";
2
- import MVT from "ol/format/MVT.js";
3
- import TileState from "ol/TileState.js";
4
- import VectorTileSource from "ol/source/VectorTile.js";
5
- import mitt from "mitt";
6
- import { WMTS, XYZ } from "ol/source.js";
7
- import TileLayer from "ol/layer/Tile.js";
8
- import { GeoJSON, WMTSCapabilities } from "ol/format.js";
9
- import { optionsFromCapabilities } from "ol/source/WMTS.js";
10
- import { TileGrid } from "ol/tilegrid.js";
11
- import { Tile } from "ol/layer.js";
12
- import WMTSTileGrid from "ol/tilegrid/WMTS.js";
13
- import VectorImageLayer from "ol/layer/Image.js";
14
- import RasterSource from "ol/source/Raster.js";
15
- import Map$1 from "ol/Map.js";
16
- import View from "ol/View.js";
17
- import { defaults } from "ol/control.js";
18
- import gcoord from "gcoord";
19
- import { Feature, Overlay } from "ol";
20
- import { Select, defaults as defaults$1 } from "ol/interaction.js";
21
- import { boundingExtent, getCenter } from "ol/extent.js";
22
- import { singleClick } from "ol/events/condition.js";
23
- import Feature$1 from "ol/Feature.js";
24
- import Point from "ol/geom/Point.js";
25
- import VectorLayer from "ol/layer/Vector.js";
26
- import VectorSource from "ol/source/Vector.js";
27
- import { Circle, Fill, Icon, RegularShape, Stroke, Style, Text } from "ol/style.js";
28
- import Draw from "ol/interaction/Draw.js";
29
- import { getArea, getLength } from "ol/sphere.js";
30
- import GeoJSON$1 from "ol/format/GeoJSON.js";
31
- import { Point as Point$1 } from "ol/geom.js";
32
- import Cluster from "ol/source/Cluster.js";
33
- import CircleStyle from "ol/style/Circle.js";
34
-
35
- //#region src/core/types.ts
36
- let GeometryType = /* @__PURE__ */ function(GeometryType) {
37
- GeometryType["LineString"] = "LineString";
38
- GeometryType["MultiLineString"] = "MultiLineString";
39
- GeometryType["Polygon"] = "Polygon";
40
- GeometryType["MultiPolygon"] = "MultiPolygon";
41
- GeometryType["Invalid"] = "Invalid";
42
- return GeometryType;
43
- }({});
44
-
45
- //#endregion
46
- //#region src/tdt-vts/pk.ts
47
- const PK_DIGIT_TO_CHAR = [
48
- "a",
49
- "d",
50
- "g",
51
- "j",
52
- "m"
53
- ];
54
- const PK_CHAR_TO_DIGIT = Object.freeze({
55
- a: 0,
56
- d: 1,
57
- g: 2,
58
- j: 3,
59
- m: 4
60
- });
61
- const PERTURBED_POSITIONS = [
62
- 9,
63
- 8,
64
- 7,
65
- 6
66
- ];
67
- function assertTileInteger(name, value) {
68
- if (!Number.isSafeInteger(value) || value < 0) throw new TypeError(`${name} 必须是非负安全整数`);
69
- }
70
- /**
71
- * 将 XYZ 瓦片坐标编码为天地图 VTS 使用的 pk 参数。
72
- *
73
- * 算法按 main.0f98e6f9.js 中的实现还原:
74
- * 1. 从 bit=z 到 bit=0 交织 x/y 位;
75
- * 2. 反转;
76
- * 3. 将 0-based 位置 9、8、7、6 加一;
77
- * 4. 0/1/2/3/4 映射为 a/d/g/j/m;
78
- * 5. 连续字符使用“额外重复次数”压缩。
79
- */
80
- const encodeTdtPk = (x, y, z) => {
81
- assertTileInteger("x", x);
82
- assertTileInteger("y", y);
83
- assertTileInteger("z", z);
84
- if (z > 30) throw new RangeError("当前实现将 z 限制为 0~30;天地图底图通常不超过 18 级");
85
- const maxCoordinate = 2 ** z;
86
- if (x >= maxCoordinate || y >= maxCoordinate) throw new RangeError(`z=${z} 时 x/y 必须小于 ${maxCoordinate}`);
87
- const bigX = BigInt(x);
88
- const bigY = BigInt(y);
89
- const digits = [];
90
- for (let bit = z; bit >= 0; bit -= 1) {
91
- const mask = 1n << BigInt(bit);
92
- let digit = 0;
93
- if ((bigX & mask) !== 0n) digit |= 1;
94
- if ((bigY & mask) !== 0n) digit |= 2;
95
- digits.push(digit);
96
- }
97
- digits.reverse();
98
- for (const position of PERTURBED_POSITIONS) if (position < digits.length) {
99
- const value = digits[position];
100
- if (value === void 0) throw new Error("pk 中间状态异常");
101
- digits[position] = value + 1;
102
- }
103
- let mapped = "";
104
- for (const digit of digits) {
105
- const char = PK_DIGIT_TO_CHAR[digit];
106
- if (!char) throw new Error(`无法映射 pk 数字:${digit}`);
107
- mapped += char;
108
- }
109
- return mapped.replace(/([adgjm])\1+/g, (run) => {
110
- return `${run[0]}${run.length - 1}`;
111
- });
112
- };
113
- /** 反解 pk;用于调试、单元测试和核对网络请求。 */
114
- const decodeTdtPk = (pk) => {
115
- if (!pk) throw new TypeError("pk 不能为空");
116
- const transformed = [];
117
- let cursor = 0;
118
- while (cursor < pk.length) {
119
- const char = pk[cursor];
120
- if (!char || !(char in PK_CHAR_TO_DIGIT)) throw new Error(`pk 包含非法字符:${String(char)}`);
121
- cursor += 1;
122
- let repeatText = "";
123
- while (cursor < pk.length && /\d/.test(pk[cursor] ?? "")) {
124
- repeatText += pk[cursor];
125
- cursor += 1;
126
- }
127
- const extraRepeats = repeatText ? Number(repeatText) : 0;
128
- if (!Number.isSafeInteger(extraRepeats) || extraRepeats < 0) throw new Error(`pk 重复次数无效:${repeatText}`);
129
- const digit = PK_CHAR_TO_DIGIT[char];
130
- if (digit === void 0) throw new Error(`pk 字符无法映射:${char}`);
131
- for (let i = 0; i < extraRepeats + 1; i += 1) transformed.push(digit);
132
- }
133
- for (const position of [
134
- 6,
135
- 7,
136
- 8,
137
- 9
138
- ]) if (position < transformed.length) {
139
- const value = transformed[position];
140
- if (value === void 0) throw new Error("pk 中间状态异常");
141
- const restored = value - 1;
142
- if (restored < 0 || restored > 3) throw new Error(`pk 在位置 ${position} 的扰动值无效`);
143
- transformed[position] = restored;
144
- }
145
- const digits = transformed.reverse();
146
- const z = digits.length - 1;
147
- if (z < 0 || z > 30) throw new RangeError(`pk 推导出的 z=${z} 超出范围`);
148
- let x = 0n;
149
- let y = 0n;
150
- for (let index = 0; index < digits.length; index += 1) {
151
- const digit = digits[index];
152
- if (digit === void 0 || digit < 0 || digit > 3) throw new Error(`pk 四叉数字无效:${String(digit)}`);
153
- const bit = BigInt(z - index);
154
- if ((digit & 1) !== 0) x |= 1n << bit;
155
- if ((digit & 2) !== 0) y |= 1n << bit;
156
- }
157
- const numberX = Number(x);
158
- const numberY = Number(y);
159
- assertTileInteger("反解 x", numberX);
160
- assertTileInteger("反解 y", numberY);
161
- return {
162
- z,
163
- x: numberX,
164
- y: numberY
165
- };
166
- };
167
- /**
168
- * 根据 OpenLayers 的 [z, x, y] 生成最终 VTS 地址,不依赖字符串替换。
169
- */
170
- const createTdtVtsUrl = (tileCoord, options) => {
171
- const [z, rawX, y] = tileCoord;
172
- const { token, type = "vt", version = "1.0", maxZoom = 18, hosts = [
173
- "/tile0",
174
- "/tile1",
175
- "/tile2"
176
- ], wrapX = true } = options;
177
- if (!token || z < 0 || z > maxZoom || y < 0 || hosts.length === 0) return;
178
- const matrixSize = 2 ** z;
179
- if (y >= matrixSize) return void 0;
180
- const x = wrapX ? (rawX % matrixSize + matrixSize) % matrixSize : rawX;
181
- if (x < 0 || x >= matrixSize) return void 0;
182
- const host = hosts[Math.abs(x + y) % hosts.length];
183
- if (!host) return void 0;
184
- const params = new URLSearchParams({
185
- t: type,
186
- tk: token,
187
- v: version,
188
- pk: encodeTdtPk(x, y, z)
189
- });
190
- return `${host.replace(/\/$/, "")}/vts?${params.toString()}`;
191
- };
192
- const redactTdtToken = (url) => {
193
- try {
194
- const parsed = new URL(url);
195
- if (parsed.searchParams.has("tk")) parsed.searchParams.set("tk", "***");
196
- return parsed.toString();
197
- } catch {
198
- return url.replace(/([?&]tk=)[^&]+/i, "$1***");
199
- }
200
- };
201
-
202
- //#endregion
203
- //#region src/tdt-vts/style-loader.ts
204
- const TDT_VECTOR_SOURCE_ID = "base-tdt-vector-tile";
205
- function cloneJson(value) {
206
- return JSON.parse(JSON.stringify(value));
207
- }
208
- async function fetchJson(url, signal) {
209
- const response = await fetch(url, {
210
- mode: "cors",
211
- credentials: "omit",
212
- cache: "default",
213
- signal
214
- });
215
- if (!response.ok) throw new Error(`样式请求失败:HTTP ${response.status} ${response.statusText}`);
216
- return response.json();
217
- }
218
- function parseStyle0(value) {
219
- if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error("style-0 不是 JSON 对象");
220
- const style = value;
221
- if (style.version !== 8) throw new Error(`style-0 version 必须为 8`);
222
- if (!style.sources || typeof style.sources !== "object") throw new Error("style-0 缺少 sources");
223
- if (!Array.isArray(style.layers)) throw new Error("style-0 缺少 layers 数组");
224
- return style;
225
- }
226
- function parseStyle1Layers(value) {
227
- if (Array.isArray(value)) return value;
228
- if (value && typeof value === "object") {
229
- const { layers } = value;
230
- if (Array.isArray(layers)) return layers;
231
- }
232
- throw new Error("style-1 必须是图层数组或带 layers 的对象");
233
- }
234
- /** 按原 bundle 的 beforeLayerId 语义逐层插入。 */
235
- const mergeTdtStyleLayers = (baseLayers, extraLayers) => {
236
- const result = baseLayers.map((layer) => cloneJson(layer));
237
- const ids = new Set(result.map((layer) => layer.id));
238
- for (const input of extraLayers) {
239
- const layer = cloneJson(input);
240
- if (!layer.id) throw new Error("style-1 存在无 id 图层");
241
- if (ids.has(layer.id)) throw new Error(`样式图层 id 重复:${layer.id}`);
242
- const beforeLayerId = typeof layer.beforeLayerId === "string" ? layer.beforeLayerId : "";
243
- delete layer.beforeLayerId;
244
- const index = beforeLayerId ? result.findIndex((candidate) => candidate.id === beforeLayerId) : -1;
245
- if (index >= 0) result.splice(index, 0, layer);
246
- else result.push(layer);
247
- ids.add(layer.id);
248
- }
249
- return result;
250
- };
251
- function resolveUrl(value, baseUrl) {
252
- try {
253
- return new URL(value, baseUrl).href;
254
- } catch {
255
- return value;
256
- }
257
- }
258
- function resolveStyleResourceUrls(style, styleUrl) {
259
- if (typeof style.glyphs === "string") style.glyphs = resolveUrl(style.glyphs, styleUrl);
260
- if (typeof style.sprite === "string") style.sprite = resolveUrl(style.sprite, styleUrl);
261
- else if (Array.isArray(style.sprite)) style.sprite = style.sprite.map((item) => {
262
- if (item && typeof item === "object" && typeof item.url === "string") return {
263
- ...item,
264
- url: resolveUrl(item.url, styleUrl)
265
- };
266
- return item;
267
- });
268
- for (const source of Object.values(style.sources)) {
269
- if (typeof source.url === "string") source.url = resolveUrl(source.url, styleUrl);
270
- if (Array.isArray(source.tiles)) source.tiles = source.tiles.map((url) => typeof url === "string" ? resolveUrl(url, styleUrl) : url);
271
- }
272
- }
273
- function removeUnsupportedLayers(layers) {
274
- const skippedLayerIds = [];
275
- return {
276
- layers: layers.filter((layer) => {
277
- if (layer.type === "pie") {
278
- skippedLayerIds.push(layer.id);
279
- return false;
280
- }
281
- return true;
282
- }),
283
- skippedLayerIds
284
- };
285
- }
286
- const loadTdtStyleBundle = async (options) => {
287
- const sourceId = options.sourceId ?? "base-tdt-vector-tile";
288
- const [raw0, raw1] = await Promise.all([fetchJson(options.style0Url, options.signal), fetchJson(options.style1Url, options.signal)]);
289
- const style0 = parseStyle0(raw0);
290
- const style1Layers = parseStyle1Layers(raw1);
291
- const style = cloneJson(style0);
292
- if (!style.sources[sourceId]) throw new Error(`style-0 中不存在 source:${sourceId}`);
293
- if (style.sources[sourceId]?.type !== "vector") throw new Error(`source ${sourceId} 不是 vector 类型`);
294
- style.layers = mergeTdtStyleLayers(style.layers, style1Layers);
295
- const filtered = removeUnsupportedLayers(style.layers);
296
- style.layers = filtered.layers;
297
- resolveStyleResourceUrls(style, options.style0Url);
298
- return {
299
- style,
300
- style0LayerCount: style0.layers.length,
301
- style1LayerCount: style1Layers.length,
302
- skippedLayerIds: filtered.skippedLayerIds
303
- };
304
- };
305
- const getConstantBackgroundColor = (style) => {
306
- for (const layer of style.layers) {
307
- if (layer.type !== "background") continue;
308
- const value = layer.paint?.["background-color"];
309
- if (typeof value === "string") return value;
310
- }
311
- };
312
-
313
- //#endregion
314
- //#region src/tdt-vts/worker-client.ts
315
- const DEFAULT_REQUEST_TIMEOUT_MS = 3e4;
316
- const createCancellationError = (message) => {
317
- const error = new Error(message);
318
- error.name = "AbortError";
319
- return error;
320
- };
321
- var TdtDecoderWorkerClient = class {
322
- restoreMode;
323
- worker;
324
- pending = /* @__PURE__ */ new Map();
325
- nextId = 1;
326
- disposed = false;
327
- requestTimeoutMs;
328
- constructor(restoreMode, options = {}) {
329
- this.restoreMode = restoreMode;
330
- this.requestTimeoutMs = options.requestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;
331
- if (!Number.isFinite(this.requestTimeoutMs) || this.requestTimeoutMs < 0) throw new RangeError("requestTimeoutMs 必须是非负有限数");
332
- this.worker = options.workerFactory ? options.workerFactory() : new Worker(new URL("./tdt-vts/decoder.worker.mjs", import.meta.url), {
333
- type: "module",
334
- name: "tdt-private-mvt-decoder"
335
- });
336
- this.worker.addEventListener("message", this.handleMessage);
337
- this.worker.addEventListener("error", this.handleWorkerError);
338
- }
339
- decode(url, options = {}) {
340
- if (this.disposed) return Promise.reject(/* @__PURE__ */ new Error("解码 Worker 已销毁"));
341
- if (options.signal?.aborted) return Promise.reject(createCancellationError("解码请求已取消"));
342
- const id = this.nextId;
343
- this.nextId += 1;
344
- return new Promise((resolve, reject) => {
345
- let timeout;
346
- const handleAbort = () => {
347
- this.cancelRequest(id, createCancellationError("解码请求已取消"));
348
- };
349
- const cleanup = () => {
350
- if (timeout !== void 0) clearTimeout(timeout);
351
- options.signal?.removeEventListener("abort", handleAbort);
352
- };
353
- this.pending.set(id, {
354
- resolve,
355
- reject,
356
- cleanup
357
- });
358
- options.signal?.addEventListener("abort", handleAbort, { once: true });
359
- if (this.requestTimeoutMs > 0) timeout = setTimeout(() => {
360
- this.cancelRequest(id, /* @__PURE__ */ new Error("解码请求超时"));
361
- }, this.requestTimeoutMs);
362
- const request = {
363
- type: "decode-tile",
364
- id,
365
- url,
366
- restoreMode: this.restoreMode
367
- };
368
- try {
369
- this.worker.postMessage(request);
370
- } catch (cause) {
371
- this.takePending(id)?.reject(cause instanceof Error ? cause : new Error(String(cause)));
372
- }
373
- });
374
- }
375
- dispose() {
376
- if (this.disposed) return;
377
- this.disposed = true;
378
- this.worker.removeEventListener("message", this.handleMessage);
379
- this.worker.removeEventListener("error", this.handleWorkerError);
380
- this.worker.terminate();
381
- const error = /* @__PURE__ */ new Error("解码 Worker 已销毁");
382
- for (const request of this.pending.values()) {
383
- request.cleanup();
384
- request.reject(error);
385
- }
386
- this.pending.clear();
387
- }
388
- takePending(id) {
389
- const pending = this.pending.get(id);
390
- if (!pending) return void 0;
391
- this.pending.delete(id);
392
- pending.cleanup();
393
- return pending;
394
- }
395
- cancelRequest(id, error) {
396
- const pending = this.takePending(id);
397
- if (!pending) return;
398
- const request = {
399
- type: "cancel-tile",
400
- id
401
- };
402
- try {
403
- this.worker.postMessage(request);
404
- } catch {}
405
- pending.reject(error);
406
- }
407
- fail(error) {
408
- if (this.disposed) return;
409
- this.disposed = true;
410
- this.worker.removeEventListener("message", this.handleMessage);
411
- this.worker.removeEventListener("error", this.handleWorkerError);
412
- this.worker.terminate();
413
- for (const request of this.pending.values()) {
414
- request.cleanup();
415
- request.reject(error);
416
- }
417
- this.pending.clear();
418
- }
419
- handleMessage = (event) => {
420
- const response = event.data;
421
- if (response.type !== "decode-result") return;
422
- const pending = this.takePending(response.id);
423
- if (!pending) return;
424
- if (response.ok) pending.resolve({
425
- buffer: response.buffer,
426
- stats: response.stats
427
- });
428
- else pending.reject(new Error(response.error));
429
- };
430
- handleWorkerError = (event) => {
431
- const error = new Error(event.message || "解码 Worker 发生未捕获错误");
432
- this.fail(error);
433
- };
434
- };
435
-
436
- //#endregion
437
- //#region src/tdt-vts/source.ts
438
- const createTdtVectorSource = (options) => {
439
- const decoder = new TdtDecoderWorkerClient(options.restoreMode, options.decoderOptions);
440
- const format = new MVT({ layerName: "mvt:layer" });
441
- const source = new VectorTileSource({
442
- projection: "EPSG:3857",
443
- minZoom: options.minZoom ?? 1,
444
- maxZoom: options.maxZoom ?? 18,
445
- tileSize: 512,
446
- wrapX: true,
447
- format,
448
- attributions: "© <a href=\"https://www.tianditu.gov.cn/\" target=\"_blank\" rel=\"noopener\">天地图</a>",
449
- tileUrlFunction(tileCoord) {
450
- if (!tileCoord) return void 0;
451
- const [z, x, y] = tileCoord;
452
- if (z === void 0 || x === void 0 || y === void 0) return void 0;
453
- return createTdtVtsUrl([
454
- z,
455
- x,
456
- y
457
- ], {
458
- token: options.token,
459
- maxZoom: options.maxZoom ?? 18,
460
- wrapX: true
461
- });
462
- },
463
- tileLoadFunction(tile, url) {
464
- const vectorTile = tile;
465
- vectorTile.setLoader(async (extent, _resolution, projection) => {
466
- const redactedUrl = redactTdtToken(url);
467
- try {
468
- const decoded = await decoder.decode(url);
469
- const features = format.readFeatures(decoded.buffer, {
470
- extent,
471
- featureProjection: projection
472
- });
473
- vectorTile.setFeatures(features);
474
- options.diagnostics?.onDecoded?.(decoded.stats, redactedUrl);
475
- } catch (cause) {
476
- const error = cause instanceof Error ? cause : new Error(String(cause));
477
- options.diagnostics?.onError?.(error, redactedUrl);
478
- console.error("天地图私有 VTS 加载失败", redactedUrl, error);
479
- vectorTile.setState(TileState.ERROR);
480
- }
481
- });
482
- }
483
- });
484
- const disposeSource = source.dispose.bind(source);
485
- source.dispose = () => {
486
- decoder.dispose();
487
- disposeSource();
488
- };
489
- return {
490
- source,
491
- decoder
492
- };
493
- };
494
-
495
- //#endregion
496
- //#region src/core/emitter.ts
497
- const emitter = mitt();
498
-
499
- //#endregion
500
- //#region src/core/map-context.ts
501
- /** 当前 OpenLayers 单例。集中存放以避免业务模块之间互相引用。 */
502
- let instance;
503
- /** @internal 更新当前地图单例。 */
504
- const setMapInstance = (map) => {
505
- instance = map;
506
- };
507
- /** @internal 清空当前地图单例。 */
508
- const clearMapInstance = () => {
509
- instance = void 0;
510
- };
511
-
512
- //#endregion
513
- //#region src/core/helper.ts
514
- const hexToRgba = (hex, alpha) => {
515
- if (hex.startsWith("#")) {
516
- let tempHex = hex.replace("#", "");
517
- if (tempHex.length === 3) tempHex = tempHex[0] + tempHex[0] + tempHex[1] + tempHex[1] + tempHex[2] + tempHex[2];
518
- return `rgba(${parseInt(tempHex.substring(0, 2), 16)}, ${parseInt(tempHex.substring(2, 4), 16)}, ${parseInt(tempHex.substring(4, 6), 16)}, ${alpha})`;
519
- } else return hex;
520
- };
521
- const getLayersDataList = () => instance?.getAllLayers().map((layer) => layer.get("_dataList") || []).flat(2) || [];
522
- const determineGeometryType = (coordinates) => {
523
- const getArrayDepth = (arr) => {
524
- if (!Array.isArray(arr) || arr.length === 0) return 0;
525
- return 1 + getArrayDepth(arr[0]);
526
- };
527
- const isClosedRing = (coords) => {
528
- if (!Array.isArray(coords) || coords.length < 3) return false;
529
- const first = coords[0];
530
- const last = coords[coords.length - 1];
531
- return first[0] === last[0] && first[1] === last[1];
532
- };
533
- const isValidCoordinate = (coord) => {
534
- return Array.isArray(coord) && coord.length >= 2 && typeof coord[0] === "number" && typeof coord[1] === "number";
535
- };
536
- const isValidLineString = (coords) => {
537
- return Array.isArray(coords) && coords.length >= 2 && coords.every(isValidCoordinate);
538
- };
539
- const isValidPolygon = (coords) => {
540
- if (!Array.isArray(coords)) return false;
541
- return (getArrayDepth(coords) === 2 ? [coords] : coords).every((ring) => Array.isArray(ring) && ring.length >= 4 && isClosedRing(ring) && ring.every(isValidCoordinate));
542
- };
543
- const depth = getArrayDepth(coordinates);
544
- try {
545
- if (depth < 2 || !coordinates || coordinates.length === 0) return "Invalid";
546
- else if (depth === 2 && isValidLineString(coordinates)) return isClosedRing(coordinates) && coordinates.length >= 4 ? "Polygon" : "LineString";
547
- else if (depth === 3 && Array.isArray(coordinates) && coordinates.every(isValidLineString)) {
548
- if (isValidPolygon(coordinates)) return "Polygon";
549
- else if (coordinates.every((arr) => !isClosedRing(arr))) return "MultiLineString";
550
- else return "Invalid";
551
- } else if (depth === 4 && Array.isArray(coordinates) && coordinates.every((rings) => isValidPolygon(rings))) return "MultiPolygon";
552
- else return "Invalid";
553
- } catch (error) {
554
- console.error("Error analyzing coordinates:", error);
555
- return "Invalid";
556
- }
557
- };
558
-
559
- //#endregion
560
- //#region src/layer/ogc-utils.ts
561
- const parser = new WMTSCapabilities();
562
- const capabilitiesMap = /* @__PURE__ */ new Map();
563
- const getCapabilities = (url) => {
564
- return new Promise((resolve) => {
565
- const capabilities = capabilitiesMap.get(url);
566
- if (capabilities) resolve(capabilities);
567
- else fetch(url).then((data) => data.text()).then((text) => {
568
- const capabilities = parser.read(text);
569
- capabilitiesMap.set(url, capabilities);
570
- resolve(capabilities);
571
- });
572
- });
573
- };
574
- /**
575
- * 创建WMTS图层
576
- * @param serverUrl
577
- */
578
- const createWmtsLayer = (serverUrl) => async (config) => {
579
- const { style, format, version, layerName, matrixSet } = config;
580
- const [workspace, datastore] = layerName.split(":");
581
- const capabilitiesUrl = `${serverUrl}/${workspace}/${datastore}/gwc/service/wmts?request=GetCapabilities`;
582
- return new Promise((resolve, reject) => {
583
- getCapabilities(capabilitiesUrl).then((data) => {
584
- const options = optionsFromCapabilities(data, {
585
- Style: style || "",
586
- layer: datastore,
587
- version: version || "1.1.0",
588
- format: format || "image/png",
589
- matrixSet: matrixSet || "EPSG:4326"
590
- });
591
- if (options) resolve(new TileLayer({
592
- zIndex: 3,
593
- className: layerName,
594
- source: new WMTS(options)
595
- }));
596
- else reject(/* @__PURE__ */ new Error("Unable to create TileLayer"));
597
- });
598
- });
599
- };
600
-
601
- //#endregion
602
- //#region src/core/constants.ts
603
- /**
604
- * 天地图默认访问令牌(仅作缺省值,正式项目请通过参数 / config 覆盖)
605
- */
606
- const DEFAULT_TIANDITU_TK = "c77eddc667c5bed016fded560baf93e7";
607
- /**
608
- * 浙江省天地图默认访问令牌(仅作缺省值,正式项目请通过参数覆盖)
609
- */
610
- const DEFAULT_ZHEJIANG_TOKEN = "3b0cdd02-c366-4363-b6de-081a9f12b364";
611
-
612
- //#endregion
613
- //#region src/layer/layer-lifecycle.ts
614
- /** @internal 从地图移除图层并释放其数据源与自身资源。 */
615
- const disposeLayer = (map, layer) => {
616
- const dynamicMarkerTimer = layer.get("_dynamicMarkerTimer");
617
- if (dynamicMarkerTimer !== void 0) {
618
- clearTimeout(dynamicMarkerTimer);
619
- layer.unset("_dynamicMarkerTimer");
620
- }
621
- layer.getSource()?.dispose();
622
- map.removeLayer(layer);
623
- layer.dispose();
624
- };
625
-
626
- //#endregion
627
- //#region src/layer/base-layer.ts
628
- /**
629
- * 创建杭州规自地图
630
- * @param layerName - 'hzsyraster' | 'hzsyvector' | 'hzsyvector_dark'
631
- */
632
- const createBaseLayer = (layerName = "hzsyvector_dark") => {
633
- const config = {
634
- zIndex: -5,
635
- preload: 1,
636
- minZoom: 8,
637
- maxZoom: 21,
638
- source: new XYZ({
639
- tileGrid: new TileGrid({
640
- tileSize: 256,
641
- resolutions: [
642
- .00549933137253187,
643
- .00274966568626595,
644
- .00137483284313297,
645
- .000687416421566485,
646
- .000343708210783242,
647
- .000171854105391621,
648
- 859270526958104e-19,
649
- 429635263479052e-19,
650
- 214817631739526e-19,
651
- 107408815869763e-19,
652
- 537044079348816e-20,
653
- 268522039674407e-20,
654
- 134261019837204e-20
655
- ],
656
- origin: [118.122911693886, 31.2869311022836],
657
- extent: [
658
- 116.43263049006,
659
- 28.6201742126026,
660
- 123.705594256347,
661
- 31.9765041681924
662
- ]
663
- }),
664
- projection: "EPSG:4326",
665
- urls: [
666
- `https://ywtg.citybrain.hangzhou.gov.cn/${layerName}/{z}/{y}/{x}`,
667
- `https://ywtg.citybrain.hangzhou.gov.cn:9456/${layerName}/{z}/{y}/{x}`,
668
- `https://ywtg.citybrain.hangzhou.gov.cn:9457/${layerName}/{z}/{y}/{x}`,
669
- `https://ywtg.citybrain.hangzhou.gov.cn:9458/${layerName}/{z}/{y}/{x}`,
670
- `https://ywtg.citybrain.hangzhou.gov.cn:9459/${layerName}/{z}/{y}/{x}`
671
- ]
672
- }),
673
- properties: {
674
- _isBaseLayer: true,
675
- _layerName: layerName
676
- }
677
- };
678
- return new Tile(config);
679
- };
680
- /**
681
- * 创建全国天地图
682
- * @param layerName - 图层名称
683
- * @param tk - 天地图TK
684
- */
685
- const createMapWorldLayer = (layerName, tk = DEFAULT_TIANDITU_TK) => {
686
- const [name, matrixSet] = layerName.split("_");
687
- const config = {
688
- preload: 1,
689
- zIndex: -10,
690
- cacheSize: 20240,
691
- properties: {
692
- _isBaseLayer: true,
693
- _layerName: layerName
694
- },
695
- source: new WMTS({
696
- crossOrigin: "anonymous",
697
- projection: "EPSG:4326",
698
- wrapX: true,
699
- layer: name,
700
- style: "default",
701
- version: "1.0.0",
702
- format: "image/png",
703
- matrixSet,
704
- tileGrid: new WMTSTileGrid({
705
- tileSize: 256,
706
- origin: [-180, 90],
707
- matrixIds: [
708
- "1",
709
- "2",
710
- "3",
711
- "4",
712
- "5",
713
- "6",
714
- "7",
715
- "8",
716
- "9",
717
- "10",
718
- "11",
719
- "12",
720
- "13",
721
- "14",
722
- "15",
723
- "16",
724
- "17",
725
- "18",
726
- "19"
727
- ],
728
- resolutions: [
729
- .703125,
730
- .3515625,
731
- .17578125,
732
- .087890625,
733
- .0439453125,
734
- .02197265625,
735
- .010986328125,
736
- .0054931640625,
737
- .00274658203125,
738
- .001373291015625,
739
- .0006866455078125,
740
- .00034332275390625,
741
- .000171661376953125,
742
- 858306884765625e-19,
743
- 4291534423828125e-20,
744
- 21457672119140625e-21,
745
- 10728836059570312e-21,
746
- 5364418029785156e-21,
747
- 2682209014892578e-21
748
- ]
749
- }),
750
- url: `https://t{0-7}.tianditu.gov.cn/${layerName}/wmts?tk=${tk}`
751
- })
752
- };
753
- return new Tile(config);
754
- };
755
- /**
756
- * 创建天地图矢量/影像底图组合。
757
- * @param tk - 天地图访问令牌
758
- */
759
- const baseLayers = (tk) => ({
760
- baseImgLayer: new Tile({
761
- className: "img-layer",
762
- source: new XYZ({
763
- crossOrigin: "anonymous",
764
- tileSize: 256,
765
- url: `https://t{0-7}.tianditu.gov.cn/DataServer?T=img_w&x={x}&y={y}&l={z}&tk=${tk}`
766
- }),
767
- zIndex: 1
768
- }),
769
- baseCiaLayer: new Tile({
770
- className: "img-layer",
771
- source: new XYZ({
772
- crossOrigin: "anonymous",
773
- tileSize: 256,
774
- url: `https://t{0-7}.tianditu.gov.cn/DataServer?T=cia_w&x={x}&y={y}&l={z}&tk=${tk}`
775
- }),
776
- zIndex: 2
777
- }),
778
- baseVecLayer: new Tile({
779
- className: "vec-layer",
780
- source: new XYZ({
781
- crossOrigin: "anonymous",
782
- tileSize: 256,
783
- url: `https://t{0-7}.tianditu.gov.cn/DataServer?T=vec_w&x={x}&y={y}&l={z}&tk=${tk}`
784
- }),
785
- zIndex: 1
786
- }),
787
- baseCvaLayer: new Tile({
788
- className: "vec-layer",
789
- source: new XYZ({
790
- crossOrigin: "anonymous",
791
- tileSize: 256,
792
- url: `https://t{0-7}.tianditu.gov.cn/DataServer?T=cva_w&x={x}&y={y}&l={z}&tk=${tk}`
793
- }),
794
- zIndex: 2
795
- })
796
- });
797
- /**
798
- * 浙江省天地图
799
- * @param layerName - 图层名称
800
- * @param tk -
801
- */
802
- const createZheJiangLayer = (layerName = "emap", tk = DEFAULT_ZHEJIANG_TOKEN) => {
803
- return new Tile({
804
- preload: 1,
805
- minZoom: 4,
806
- maxZoom: 20,
807
- zIndex: -10,
808
- cacheSize: 20240,
809
- source: new XYZ({
810
- wrapX: true,
811
- projection: "EPSG:4326",
812
- crossOrigin: "anonymous",
813
- url: `https://ditu{0-6}.zjzwfw.gov.cn/services/wmts/${layerName}/default/oss/getTile/{z}/{y}/{x}?token=${tk}`
814
- })
815
- });
816
- };
817
- /**
818
- * 根据图层名称获取图层
819
- * @param name
820
- */
821
- const getLayerByName = (name) => instance.getAllLayers().find((item) => (item.get("_layerName") || item.getClassName()) === name);
822
- /**
823
- * 根据图层名称删除图层
824
- * @param name
825
- */
826
- const removeLayerByName = (name) => {
827
- const layer = getLayerByName(name);
828
- if (layer) disposeLayer(instance, layer);
829
- };
830
- /**
831
- * 切换图层的显示、隐藏
832
- * @param activeNames
833
- * @param allLayerNames
834
- */
835
- const changeLayersByNames = (activeNames, allLayerNames) => {
836
- instance.getAllLayers().filter((item) => allLayerNames.includes(item.getClassName())).forEach((layer) => {
837
- layer.setVisible(activeNames?.includes(layer.getClassName()));
838
- });
839
- };
840
-
841
- //#endregion
842
- //#region src/layer/raster-layer.ts
843
- const getDefaultThreadCount = () => Math.max(1, Math.min(4, globalThis.navigator?.hardwareConcurrency ?? 2));
844
- /** 使用 RGB 比例和目标颜色转换栅格图层。 */
845
- const changeSource = (layer, rgbColorArray, ratioArray, options = {}) => {
846
- const rasterSource = new RasterSource({
847
- sources: [layer],
848
- operationType: "image",
849
- operation: (pixels, data) => {
850
- const reverseFunc = (pixelArray, operationData) => {
851
- const operationRatioArray = operationData.ratioArray;
852
- const operationRgbColorArray = operationData.rgbColorArray;
853
- if (operationRatioArray && operationRgbColorArray) {
854
- const [redRatio = 0, greenRatio = 0, blueRatio = 0] = operationRatioArray;
855
- for (let index = 0; index < pixelArray.length; index += 4) {
856
- const red = pixelArray[index];
857
- const green = pixelArray[index + 1];
858
- const blue = pixelArray[index + 2];
859
- const grey = red * redRatio + green * greenRatio + blue * blueRatio;
860
- pixelArray[index] = operationRgbColorArray[0] - grey;
861
- pixelArray[index + 1] = operationRgbColorArray[1] - grey;
862
- pixelArray[index + 2] = operationRgbColorArray[2] - grey;
863
- }
864
- }
865
- };
866
- reverseFunc(pixels[0].data, data);
867
- return pixels[0];
868
- },
869
- threads: options.threads ?? getDefaultThreadCount()
870
- });
871
- rasterSource.on("beforeoperations", (event) => {
872
- event.data.ratioArray = ratioArray;
873
- event.data.rgbColorArray = rgbColorArray;
874
- });
875
- return rasterSource;
876
- };
877
- /** 使用 changeSource 创建可显示的 VectorImageLayer。 */
878
- const changeLayer = (layer, rgbColorArray, ratioArray, options = {}) => new VectorImageLayer({ source: changeSource(layer, rgbColorArray, ratioArray, options) });
879
-
880
- //#endregion
881
- //#region src/overlay/overlay.ts
882
- /**
883
- * 移除带指定属性键的 Overlay(默认按 'position')
884
- * @param key
885
- */
886
- const removeOverlays = (key = "") => {
887
- if (instance) instance.getOverlays().getArray().filter((overlay) => overlay.get(key || "position")).forEach((overlay) => instance.removeOverlay(overlay));
888
- };
889
- const removeAnimationMarker = () => removeOverlays("_isAnimationMarker");
890
- /**
891
- * 创建动画标注 Overlay
892
- * @param imgUrl
893
- * @param data
894
- */
895
- const createAnimationMarker = (imgUrl, data) => {
896
- removeAnimationMarker();
897
- const { width, height, lnglat, crsForm } = data;
898
- const element = document.createElement("div");
899
- const image = document.createElement("img");
900
- image.src = imgUrl;
901
- image.alt = "";
902
- if (width !== void 0) image.width = width;
903
- if (height !== void 0) image.height = height;
904
- element.append(image);
905
- const overlay = new Overlay({
906
- element,
907
- stopEvent: false,
908
- positioning: "bottom-center",
909
- position: gcoord.transform([+lnglat[0], +lnglat[1]], crsForm || gcoord.WGS84, gcoord.WGS84)
910
- });
911
- overlay.set("_isAnimationMarker", true);
912
- instance.addOverlay(overlay);
913
- };
914
- /**
915
- * 创建 Overlay
916
- * @param config
917
- */
918
- const createOverlay = (config = {
919
- id: "popup-container",
920
- positioning: "top-center"
921
- }) => {
922
- return new Overlay(Object.assign({ element: document.getElementById(config.id) || void 0 }, config));
923
- };
924
-
925
- //#endregion
926
- //#region src/layer/layer-manager.ts
927
- /**
928
- * 重置激活的矢量数据(清空 overlay 与各图层选择交互的要素集合)
929
- */
930
- const resetActiveVector = () => {
931
- removeOverlays();
932
- instance.getAllLayers().forEach((layer) => {
933
- layer.get("_selectInteraction")?.getFeatures().clear();
934
- });
935
- };
936
- /**
937
- * 移除覆盖物以及所有非底图自定义图层
938
- */
939
- const removeAllLayers = () => {
940
- removeOverlays();
941
- if (instance) instance.getAllLayers().forEach((layer) => {
942
- if (!layer.get("_isBaseLayer")) disposeLayer(instance, layer);
943
- });
944
- };
945
- /**
946
- * 重置激活 Feature
947
- */
948
- const resetActiveFeature = () => {
949
- removeOverlays();
950
- instance.getAllLayers().filter((layer) => layer.get("_isVectorLayer")).forEach((layer) => {
951
- const selectInteraction = layer.get("_selectInteraction");
952
- if (selectInteraction) {
953
- selectInteraction.dispatchEvent({
954
- type: "select",
955
- selected: [],
956
- mapBrowserEvent: void 0,
957
- deselected: selectInteraction.getFeatures().getArray()
958
- });
959
- selectInteraction.getFeatures().clear();
960
- }
961
- });
962
- };
963
- /**
964
- * 按 _uuid 激活要素
965
- * @param uid
966
- */
967
- const activeFeatureByUid = (uid) => {
968
- const targetLayers = instance.getAllLayers().filter((layer) => layer.get("_isVectorLayer"));
969
- if (targetLayers.length) {
970
- const targetLayer = targetLayers.find((layer) => layer.get("_uuidSet").has(uid) && layer.get("_selectInteraction"));
971
- if (targetLayer) {
972
- const targetFeature = (targetLayer.getSource()?.getFeatures() || []).find((feature) => feature.get("_uuid") === uid);
973
- const selectInteraction = targetLayer.get("_selectInteraction");
974
- if (selectInteraction) {
975
- const collection = selectInteraction.getFeatures().getArray();
976
- if (!collection.find((feature) => feature.get("_uuid") === uid)) {
977
- selectInteraction.dispatchEvent({
978
- type: "select",
979
- deselected: collection,
980
- selected: [targetFeature],
981
- mapBrowserEvent: void 0
982
- });
983
- selectInteraction.getFeatures().push(targetFeature);
984
- }
985
- }
986
- }
987
- }
988
- };
989
-
990
- //#endregion
991
- //#region src/map/create-map.ts
992
- let mapCenter = [119.71512016971076, 30.122490541963536];
993
- const createMapInstance = (config = {}) => {
994
- if (instance) return instance;
995
- const map = createMap(config);
996
- setMapInstance(map);
997
- return map;
998
- };
999
- const createMap = (config = {}) => {
1000
- const innerConfig = Object.assign({
1001
- zoom: 9.4,
1002
- maxZoom: 18,
1003
- minZoom: 9.4,
1004
- center: mapCenter,
1005
- baseLayer: "vec-layer",
1006
- projection: "EPSG:4326",
1007
- layers: [createBaseLayer()],
1008
- constrainResolution: false,
1009
- smoothExtentConstraint: false,
1010
- tk: DEFAULT_TIANDITU_TK
1011
- }, config || {});
1012
- const map = new Map$1({
1013
- view: new View(innerConfig),
1014
- controls: defaults({
1015
- zoom: false,
1016
- rotate: false,
1017
- attribution: false
1018
- }),
1019
- interactions: defaults$1({
1020
- pinchRotate: false,
1021
- doubleClickZoom: false,
1022
- altShiftDragRotate: false
1023
- }),
1024
- layers: innerConfig.layers || [createBaseLayer()]
1025
- });
1026
- mapCenter = innerConfig.center || mapCenter;
1027
- map.activeBaseLayer = innerConfig.baseLayer;
1028
- map.getLayerByName = getLayerByName;
1029
- map.removeLayerByName = removeLayerByName;
1030
- map.changeLayersByNames = changeLayersByNames;
1031
- let pointerMoveFrame;
1032
- let latestPixel;
1033
- const cancelPointerMoveFrame = () => {
1034
- if (pointerMoveFrame === void 0) return;
1035
- if (typeof globalThis.cancelAnimationFrame === "function") globalThis.cancelAnimationFrame(pointerMoveFrame);
1036
- else clearTimeout(pointerMoveFrame);
1037
- pointerMoveFrame = void 0;
1038
- };
1039
- map.on("pointermove", (evt) => {
1040
- latestPixel = evt.pixel;
1041
- if (pointerMoveFrame !== void 0) return;
1042
- const updateCursor = () => {
1043
- pointerMoveFrame = void 0;
1044
- if (!latestPixel) return;
1045
- map.getTargetElement().style.cursor = map.hasFeatureAtPixel(latestPixel) ? "pointer" : "";
1046
- };
1047
- pointerMoveFrame = typeof globalThis.requestAnimationFrame === "function" ? globalThis.requestAnimationFrame(updateCursor) : setTimeout(updateCursor, 16);
1048
- });
1049
- map.set("_disposePointerMove", cancelPointerMoveFrame);
1050
- return map;
1051
- };
1052
- const disposeMap = () => {
1053
- if (!instance) return;
1054
- const map = instance;
1055
- map.get("_disposePointerMove")?.();
1056
- map.unset("_disposePointerMove");
1057
- map.getOverlays().forEach((overlay) => {
1058
- map.removeOverlay(overlay);
1059
- });
1060
- map.getInteractions().clear();
1061
- map.getAllLayers().forEach((layer) => disposeLayer(map, layer));
1062
- map.setTarget("");
1063
- map.dispose();
1064
- clearMapInstance();
1065
- };
1066
- /** 兼容别名;复用 map-ol-utils 现有的完整销毁语义。 */
1067
- const disposeInstance = () => disposeMap();
1068
- const getMapInstance = () => instance;
1069
- const resetZoomAndCenter = (center = mapCenter) => {
1070
- if (instance) instance.getView().animate({
1071
- zoom: 9.4,
1072
- center,
1073
- duration: 1e3
1074
- });
1075
- };
1076
- const resetMap = (config = {
1077
- center: mapCenter,
1078
- isClearAll: true
1079
- }) => {
1080
- removeAllLayers();
1081
- resetZoomAndCenter(config.center || mapCenter);
1082
- };
1083
-
1084
- //#endregion
1085
- //#region src/map/view-fit.ts
1086
- /** 视野自适应的默认参数(padding + 动画时长) */
1087
- const DEFAULT_FIT_OPTIONS = {
1088
- padding: [
1089
- 50,
1090
- 50,
1091
- 50,
1092
- 50
1093
- ],
1094
- duration: 1e3
1095
- };
1096
- /**
1097
- * 根据 Array<Coordinate> Fit
1098
- * @param coords
1099
- * @param mapInstance
1100
- * @param fitOptions
1101
- */
1102
- const setFitViewByCoords = (coords, mapInstance = instance, fitOptions) => {
1103
- const view = mapInstance.getView();
1104
- if (coords.length === 1) view.animate({
1105
- zoom: 14,
1106
- duration: 1e3,
1107
- center: coords[0],
1108
- ...fitOptions || {}
1109
- });
1110
- else {
1111
- const extent = boundingExtent(coords);
1112
- view.fit(extent, {
1113
- ...DEFAULT_FIT_OPTIONS,
1114
- ...fitOptions || {}
1115
- });
1116
- }
1117
- };
1118
- /**
1119
- * 根据 SimpleGeometry Fit
1120
- * @param geom
1121
- * @param mapInstance
1122
- * @param fitOptions
1123
- */
1124
- const setFitViewByGeom = (geom, mapInstance = instance, fitOptions) => {
1125
- mapInstance.getView().fit(geom, {
1126
- ...DEFAULT_FIT_OPTIONS,
1127
- ...fitOptions || {}
1128
- });
1129
- };
1130
- /**
1131
- * 根据 VectorLayer 要素的四至信息 Fit
1132
- * @param layer
1133
- * @param mapInstance
1134
- * @param fitOptions
1135
- */
1136
- const setFitViewByLayer = (layer, mapInstance = instance, fitOptions) => {
1137
- const source = layer.getSource();
1138
- if (!source || source.isEmpty()) return;
1139
- const extent = source.getExtent();
1140
- mapInstance.getView().fit(extent, {
1141
- ...DEFAULT_FIT_OPTIONS,
1142
- ...fitOptions || {}
1143
- });
1144
- };
1145
- /** 使用两段缩放动画飞到目标位置。 */
1146
- const flyToAnimate = (center, zoom) => {
1147
- const view = instance.getView();
1148
- const currentZoom = view.getZoom();
1149
- view.animate({
1150
- center,
1151
- duration: 2e3
1152
- });
1153
- if (!zoom) view.animate({
1154
- zoom: currentZoom - 2,
1155
- duration: 1e3
1156
- }, {
1157
- zoom: currentZoom + 2 > 12 ? 12 : currentZoom + 2,
1158
- duration: 1e3
1159
- });
1160
- else view.animate({
1161
- zoom: zoom - 2,
1162
- duration: 1e3
1163
- }, {
1164
- zoom,
1165
- duration: 1e3
1166
- });
1167
- };
1168
-
1169
- //#endregion
1170
- //#region src/layer/marker-layer.ts
1171
- /** 创建 Marker 文本标注。 */
1172
- const createText = (item) => new Text({
1173
- text: item.text,
1174
- offsetY: item.offsetY,
1175
- offsetX: item.offsetX,
1176
- justify: "center",
1177
- textAlign: "center",
1178
- font: "16px PingFangSC-Semibold",
1179
- fill: new Fill({ color: "rgba(255, 255, 255, 0.8)" })
1180
- });
1181
- /** 创建 Marker 默认态或激活态样式。 */
1182
- const createIconStyle = (item, active = false) => new Style({
1183
- image: new Icon({
1184
- scale: item.scale || 1,
1185
- rotation: item.rotation || 0,
1186
- anchor: item.anchor || [.5, 1],
1187
- src: active ? item.activeImg || item.img : item.img
1188
- }),
1189
- text: item.showText ? createText(item) : void 0
1190
- });
1191
- /** 创建带 data 属性约定的 Marker Feature。 */
1192
- const createMarker = (item, config) => {
1193
- const feature = new Feature$1({
1194
- geometry: new Point([+item.lng, +item.lat]),
1195
- data: Object.assign({}, item || {}, { layerName: config.layerName })
1196
- });
1197
- feature.setStyle(createIconStyle(item));
1198
- return feature;
1199
- };
1200
- /** 批量创建 Marker 矢量图层。 */
1201
- const createMarkersLayer = (dataList, config) => {
1202
- const { layerName, visible = true, zIndex = 10 } = config;
1203
- const source = new VectorSource();
1204
- dataList.forEach((item) => source.addFeature(createMarker(item, config)));
1205
- return new VectorLayer({
1206
- zIndex,
1207
- visible,
1208
- source,
1209
- className: layerName || "marker-layer"
1210
- });
1211
- };
1212
- /** @internal 按当前缩放级别刷新弹性 Marker 的图标。 */
1213
- const setElasticMarkerStyle = (featureList, currentZoom) => {
1214
- const imageKey = currentZoom >= 14 ? "largeImg" : currentZoom >= 11 ? "smallImg" : "img";
1215
- featureList.forEach((feature) => {
1216
- const config = feature.get("data");
1217
- if (!config.isSelected) feature.setStyle(createIconStyle({
1218
- ...config,
1219
- img: config[imageKey]
1220
- }, false));
1221
- });
1222
- };
1223
- /** @internal 同步 Marker 选中状态及对应图标样式。 */
1224
- const setMarkerSelectedState = (feature, selected) => {
1225
- const config = feature.get("data");
1226
- const next = {
1227
- ...config,
1228
- isSelected: selected
1229
- };
1230
- feature.set("data", next);
1231
- if (config.isElastic) setElasticMarkerStyle([feature], instance.getView().getZoom());
1232
- else feature.setStyle(createIconStyle(next, selected));
1233
- };
1234
- /** 清空外部选择集合并恢复 Marker 未选中样式。 */
1235
- const changeUnselectStyle = (featureList) => {
1236
- featureList.forEach((feature) => {
1237
- setMarkerSelectedState(feature, false);
1238
- });
1239
- featureList.clear();
1240
- };
1241
- /** 创建随缩放级别切换小图/大图的 Marker 图层。 */
1242
- const createElasticMarkerLayer = (dataList, config) => {
1243
- const elasticData = dataList.map((item) => ({
1244
- ...item,
1245
- isElastic: true
1246
- }));
1247
- const vectorLayer = createMarkersLayer(elasticData, config);
1248
- const features = vectorLayer.getSource().getFeatures();
1249
- setElasticMarkerStyle(features, instance.getView().getZoom());
1250
- instance.getView().on("change:resolution", ({ target }) => {
1251
- setElasticMarkerStyle(features, target.get("zoom"));
1252
- });
1253
- return vectorLayer;
1254
- };
1255
- /** 创建单 Marker 图层并直接加入当前地图实例。 */
1256
- const createMoveMarkerLayer = (dataItem, config) => {
1257
- const marker = createMarker(dataItem, config);
1258
- const vectorLayer = new VectorLayer({
1259
- className: config.layerName || "ol-layer",
1260
- source: new VectorSource({ features: [marker] })
1261
- });
1262
- instance.addLayer(vectorLayer);
1263
- return { vectorLayer };
1264
- };
1265
-
1266
- //#endregion
1267
- //#region src/interaction/select.ts
1268
- /**
1269
- * 处理要素选中:切换激活样式、派发事件、按需自动居中
1270
- * @param vectorLayer
1271
- * @param feature
1272
- */
1273
- const selectEventHandle = (vectorLayer, feature) => {
1274
- const data = feature.getProperties();
1275
- const { activeStyle } = data;
1276
- feature.setStyle(activeStyle);
1277
- emitter.emit("selected-feature", data);
1278
- vectorLayer.set("_hasActive", true);
1279
- if (vectorLayer.get("_isDynamic")) {
1280
- const activeImage = activeStyle.getImage();
1281
- const previousTimer = vectorLayer.get("_dynamicMarkerTimer");
1282
- if (previousTimer !== void 0) clearTimeout(previousTimer);
1283
- const timer = setTimeout(() => {
1284
- vectorLayer.unset("_dynamicMarkerTimer");
1285
- emitter.emit("dynamic-marker", Object.assign({}, data.rawData || {}, {
1286
- lnglat: data.lnglat,
1287
- style: data.style,
1288
- crsForm: data.crsForm,
1289
- width: activeImage.getWidth(),
1290
- height: activeImage.getHeight()
1291
- }));
1292
- }, 1e3);
1293
- vectorLayer.set("_dynamicMarkerTimer", timer);
1294
- }
1295
- if (vectorLayer.get("_isAutoFit")) {
1296
- const view = instance.getView();
1297
- view.animate({
1298
- duration: 1e3,
1299
- zoom: view.getZoom(),
1300
- center: getCenter(feature.getGeometry().getExtent())
1301
- });
1302
- }
1303
- };
1304
- /**
1305
- * 创建点击交互
1306
- * @param vectorLayer
1307
- */
1308
- const createSelectByLayer = (vectorLayer) => {
1309
- const interaction = new Select({
1310
- style: null,
1311
- multi: false,
1312
- hitTolerance: 10,
1313
- layers: [vectorLayer],
1314
- condition: (evt) => {
1315
- if (!singleClick(evt)) return false;
1316
- let hasFeature = false;
1317
- instance.forEachFeatureAtPixel(evt.pixel, () => {
1318
- hasFeature = true;
1319
- }, { layerFilter: (layer) => layer === vectorLayer });
1320
- return hasFeature;
1321
- }
1322
- });
1323
- interaction.on("select", (evt) => {
1324
- const { selected, deselected } = evt;
1325
- if (selected.length) {
1326
- const feature = selected[0];
1327
- if (vectorLayer.get("_isClusterLayer")) {
1328
- const features = feature.get("features");
1329
- if (features.length === 1) selectEventHandle(vectorLayer, features[0]);
1330
- } else selectEventHandle(vectorLayer, feature);
1331
- }
1332
- if (deselected.length) {
1333
- const feature = deselected[0];
1334
- const data = feature.getProperties();
1335
- feature.setStyle(data.unActiveStyle);
1336
- vectorLayer.set("_hasActive", false);
1337
- }
1338
- });
1339
- instance.addInteraction(interaction);
1340
- vectorLayer.set("_selectInteraction", interaction);
1341
- return interaction;
1342
- };
1343
- /** 按多个 className 创建选择交互。 */
1344
- const createLayerSelectByNames = (layerNames, selectedHandler, unSelectHandler) => {
1345
- const interaction = new Select({
1346
- style: null,
1347
- layers: (layer) => layerNames.includes(layer.getClassName())
1348
- });
1349
- interaction.on("select", ({ deselected, selected }) => {
1350
- if (deselected.length) {
1351
- const feature = deselected[0];
1352
- setMarkerSelectedState(feature, false);
1353
- unSelectHandler?.(feature);
1354
- }
1355
- if (selected.length) {
1356
- const feature = selected[0];
1357
- setMarkerSelectedState(feature, true);
1358
- selectedHandler?.(feature);
1359
- }
1360
- });
1361
- return interaction;
1362
- };
1363
-
1364
- //#endregion
1365
- //#region src/interaction/draw.ts
1366
- const formatArea = (polygon) => {
1367
- const area = getArea(polygon, { projection: "EPSG:4326" });
1368
- return area > 1e4 ? `${Math.round(area / 1e6 * 100) / 100} km\xB2` : `${Math.round(area * 100) / 100} m\xB2`;
1369
- };
1370
- const formatLength = (line) => {
1371
- const length = getLength(line, { projection: "EPSG:4326" });
1372
- return length > 100 ? `${Math.round(length / 1e3 * 100) / 100} km` : `${Math.round(length * 100) / 100} m`;
1373
- };
1374
- const drawDefaultStyle = new Style({
1375
- fill: new Fill({ color: "rgba(255, 255, 255, 0.2)" }),
1376
- stroke: new Stroke({
1377
- color: "rgba(0, 0, 0, 0.5)",
1378
- lineDash: [10, 10],
1379
- width: 2
1380
- }),
1381
- image: new Circle({
1382
- radius: 5,
1383
- stroke: new Stroke({ color: "rgba(0, 0, 0, 0.7)" }),
1384
- fill: new Fill({ color: "rgba(255, 255, 255, 0.2)" })
1385
- })
1386
- });
1387
- const drawLabelStyleCache = /* @__PURE__ */ new WeakMap();
1388
- const getOrCreateLabelStyle = (feature) => {
1389
- const cached = drawLabelStyleCache.get(feature);
1390
- if (cached) return cached;
1391
- const style = new Style({
1392
- text: new Text({
1393
- font: "14px",
1394
- fill: new Fill({ color: "rgba(255, 255, 255, 1)" }),
1395
- backgroundFill: new Fill({ color: "rgba(0, 0, 0, 0.7)" }),
1396
- padding: [
1397
- 3,
1398
- 3,
1399
- 3,
1400
- 3
1401
- ],
1402
- textBaseline: "bottom",
1403
- offsetY: -15
1404
- }),
1405
- image: new RegularShape({
1406
- radius: 8,
1407
- points: 3,
1408
- angle: Math.PI,
1409
- displacement: [0, 10],
1410
- fill: new Fill({ color: "rgba(0, 0, 0, 0.7)" })
1411
- })
1412
- });
1413
- drawLabelStyleCache.set(feature, style);
1414
- return style;
1415
- };
1416
- /** 为绘制中的线和面追加长度/面积标签。 */
1417
- const getDrawStyle = (feature, drawStyle = drawDefaultStyle) => {
1418
- const styles = drawStyle ? [drawStyle] : [];
1419
- const geometry = feature.getGeometry();
1420
- let point;
1421
- let label;
1422
- if (geometry.getType() === "Polygon") {
1423
- point = geometry.getInteriorPoint();
1424
- label = formatArea(geometry);
1425
- } else if (geometry.getType() === "LineString") {
1426
- point = new Point(geometry.getLastCoordinate());
1427
- label = formatLength(geometry);
1428
- }
1429
- if (point && label) {
1430
- const labelStyle = getOrCreateLabelStyle(feature);
1431
- labelStyle.setGeometry(point);
1432
- labelStyle.getText().setText(label);
1433
- styles.push(labelStyle);
1434
- }
1435
- return styles;
1436
- };
1437
- /** 创建绘制交互。 */
1438
- const createDrawInteraction = (type = "Point", vectorLayer, openTip) => new Draw({
1439
- source: vectorLayer.getSource(),
1440
- type,
1441
- style: openTip ? (feature) => getDrawStyle(feature, void 0) : void 0
1442
- });
1443
-
1444
- //#endregion
1445
- //#region src/layer/geojson-layer.ts
1446
- /** 直接从 GeoJSON 创建矢量图层。 */
1447
- const createGeoJsonLayer = (geoJson, style, config) => {
1448
- const { layerName } = config || {};
1449
- const layerConfig = Object.assign({
1450
- className: layerName || "ol-layer",
1451
- source: new VectorSource({ features: new GeoJSON$1().readFeatures(geoJson) }),
1452
- style: style || new Style({
1453
- stroke: new Stroke({
1454
- color: "red",
1455
- lineDash: [10],
1456
- width: 3
1457
- }),
1458
- fill: new Fill({ color: "rgba(0, 0, 255, 0.1)" })
1459
- })
1460
- }, config || {});
1461
- return new VectorLayer(layerConfig);
1462
- };
1463
-
1464
- //#endregion
1465
- //#region src/layer/vector-layer.ts
1466
- /**
1467
- * 按 colorKey/colorMap 解析颜色,未命中时回退到 fallback
1468
- */
1469
- const resolveColor = (data, colorKey, colorMap, fallback) => colorKey ? colorMap?.get(data[colorKey]) || fallback : fallback;
1470
- /**
1471
- * 组装业务数据为 GeoJson(坐标统一转 WGS84)
1472
- * @param dataList
1473
- * @param config
1474
- */
1475
- const assembleGeoJson = (dataList, config) => {
1476
- const { key, crsForm } = config;
1477
- const tempList = [];
1478
- dataList.forEach((item) => {
1479
- try {
1480
- const raw = item[key];
1481
- const pathArray = Array.isArray(raw) ? raw : JSON.parse(raw || "[]");
1482
- if (pathArray.length) {
1483
- const type = item["geoType"] || determineGeometryType(pathArray);
1484
- if (type !== "Invalid") tempList.push({
1485
- type: "Feature",
1486
- properties: { ...item },
1487
- geometry: {
1488
- type,
1489
- coordinates: pathArray
1490
- }
1491
- });
1492
- }
1493
- } catch (e) {
1494
- console.error(`创建线状数据失败:${e}`);
1495
- }
1496
- });
1497
- return gcoord.transform({
1498
- type: "FeatureCollection",
1499
- features: tempList
1500
- }, crsForm || gcoord.WGS84, gcoord.WGS84);
1501
- };
1502
- const createVectorStyle = (feature, config) => {
1503
- const { isLine, colorMap, fillColor, colorKey, strokeColor, fillOpacity, strokeWeight, activeFillColor, activeFillOpacity, activeStrokeColor, activeStrokeWeight } = config || {};
1504
- const data = feature.getProperties();
1505
- const activeStyle = new Style({ stroke: new Stroke({
1506
- width: activeStrokeWeight ?? strokeWeight ?? 4,
1507
- color: activeStrokeColor || resolveColor(data, colorKey, colorMap, strokeColor)
1508
- }) });
1509
- const unActiveStyle = new Style({ stroke: new Stroke({
1510
- width: strokeWeight ?? 2,
1511
- color: resolveColor(data, colorKey, colorMap, strokeColor)
1512
- }) });
1513
- if (!isLine) {
1514
- activeStyle.setFill(new Fill({ color: hexToRgba(activeFillColor || fillColor || "", activeFillOpacity ?? fillOpacity ?? 1) || hexToRgba(resolveColor(data, colorKey, colorMap, fillColor) || "", fillOpacity ?? 1) }));
1515
- unActiveStyle.setFill(new Fill({ color: hexToRgba(resolveColor(data, colorKey, colorMap, fillColor) || "", fillOpacity ?? 1) }));
1516
- }
1517
- feature.setProperties({
1518
- activeStyle,
1519
- unActiveStyle
1520
- });
1521
- return unActiveStyle;
1522
- };
1523
- const readBusinessFeatures = (dataList, config) => {
1524
- const geoJson = assembleGeoJson(dataList, config);
1525
- const features = new GeoJSON().readFeatures(geoJson);
1526
- const acceptedData = features.map((feature) => {
1527
- const data = { ...feature.getProperties() };
1528
- delete data.geometry;
1529
- return data;
1530
- });
1531
- features.forEach((feature) => {
1532
- feature.setStyle(createVectorStyle(feature, config));
1533
- });
1534
- return {
1535
- features,
1536
- acceptedData
1537
- };
1538
- };
1539
- /**
1540
- * 创建业务矢量图层(自动推断线/面,生成默认/激活样式)
1541
- * @param dataList
1542
- * @param config
1543
- */
1544
- const createBusinessLayer = (dataList, config) => {
1545
- const { isAutoFit, layerName, openClick } = config;
1546
- const { features, acceptedData } = readBusinessFeatures(dataList, config);
1547
- const vectorLayer = new VectorLayer({
1548
- zIndex: 5,
1549
- className: layerName ? layerName : "ol-layer",
1550
- source: new VectorSource({ features })
1551
- });
1552
- if (openClick) createSelectByLayer(vectorLayer);
1553
- vectorLayer.setProperties({
1554
- _isCustom: true,
1555
- _isAutoFit: isAutoFit,
1556
- _isVectorLayer: true,
1557
- _vectorConfig: config,
1558
- _layerName: layerName,
1559
- _dataList: acceptedData,
1560
- _uuidSet: new Set(acceptedData.map((item) => item._uuid))
1561
- });
1562
- return vectorLayer;
1563
- };
1564
- /**
1565
- * 追加 Features
1566
- * @param targetLayer
1567
- * @param dataList
1568
- * @param crsForm
1569
- */
1570
- const appendFeaturesByLayer = (targetLayer, dataList, crsForm) => {
1571
- const vectorSource = targetLayer.getSource();
1572
- const { _vectorConfig: config } = targetLayer.getProperties() || {};
1573
- const { features, acceptedData } = readBusinessFeatures(dataList, Object.assign({}, config, { crsForm }));
1574
- vectorSource.addFeatures(features);
1575
- const uuidSet = new Set(targetLayer.get("_uuidSet") || []);
1576
- acceptedData.forEach((item) => uuidSet.add(item._uuid));
1577
- targetLayer.set("_uuidSet", uuidSet);
1578
- targetLayer.set("_dataList", [...targetLayer.get("_dataList") || [], ...acceptedData]);
1579
- };
1580
- /**
1581
- * 创建面状图层
1582
- * @param dataList
1583
- * @param config
1584
- */
1585
- const createVectorFaceLayer = (dataList = [], config) => createBusinessLayer(dataList, Object.assign({}, config, { isLine: false }));
1586
- /**
1587
- * 创建矢量线图层
1588
- * @param dataList
1589
- * @param config
1590
- */
1591
- const createVectorLineLayer = (dataList = [], config) => createBusinessLayer(dataList, Object.assign({}, config, { isLine: true }));
1592
-
1593
- //#endregion
1594
- //#region src/layer/mass-marks.ts
1595
- /**
1596
- * 构建 MassMarks 样式数组(默认段 + 激活段,长度 statusNum * 2)
1597
- */
1598
- const buildMassMarksStyles = (getImgUrlFn) => {
1599
- return (iconConfig) => {
1600
- const { baseUrl, markerIcon, statusNum = 5, activeScale = 1.3, markerSize = [32, 32] } = iconConfig;
1601
- const getMarkerUrl = getImgUrlFn(baseUrl);
1602
- const [x, y] = markerSize;
1603
- const massMarksStyles = new Array(statusNum * 2);
1604
- for (let i = 0; i < statusNum; i++) {
1605
- const imageUrl = getMarkerUrl(`${markerIcon}-${i}`, ".webp");
1606
- massMarksStyles[i] = new Style({
1607
- zIndex: 88 + i,
1608
- image: new Icon({
1609
- width: x,
1610
- height: y,
1611
- src: imageUrl,
1612
- anchor: [.5, 1]
1613
- })
1614
- });
1615
- const activeImage = getMarkerUrl(`${markerIcon}-${i}-active`, ".webp");
1616
- massMarksStyles[i + statusNum] = new Style({
1617
- zIndex: 120,
1618
- image: new Icon({
1619
- width: x * activeScale,
1620
- height: y * activeScale,
1621
- anchor: [.5, 1],
1622
- src: activeImage ? activeImage : imageUrl
1623
- })
1624
- });
1625
- }
1626
- return massMarksStyles;
1627
- };
1628
- };
1629
- const createFeatures = (dataList, styleList = []) => {
1630
- return dataList.map((item) => {
1631
- const { crsForm } = item;
1632
- const coord = gcoord.transform([+item.lnglat[0], +item.lnglat[1]], crsForm || gcoord.WGS84, gcoord.WGS84);
1633
- const feature = new Feature({ geometry: new Point$1(coord) });
1634
- feature.setProperties({
1635
- ...item,
1636
- unActiveStyle: styleList[item.style],
1637
- activeStyle: styleList[item.style + styleList.length / 2]
1638
- });
1639
- feature.setStyle(styleList[item.style]);
1640
- return feature;
1641
- });
1642
- };
1643
- /**
1644
- * 向海量点图层增量添加 Features
1645
- * @param targetLayer
1646
- * @param dataList
1647
- * @param config
1648
- */
1649
- const addFeaturesByLayer = (targetLayer, dataList, config = {}) => {
1650
- const massMarksStyles = targetLayer.get("_massMarksStyles");
1651
- const targetSource = targetLayer?.getSource();
1652
- const { clear } = config;
1653
- const features = createFeatures(dataList, massMarksStyles);
1654
- if (clear) targetSource?.clear();
1655
- targetSource?.addFeatures(features);
1656
- const nextData = [...clear ? [] : targetLayer.get("_dataList") || [], ...dataList];
1657
- targetLayer.set("_dataList", nextData);
1658
- targetLayer.set("_uuidSet", new Set(nextData.map((item) => item._uuid)));
1659
- };
1660
- /**
1661
- * 创建 massMarksLayer
1662
- * @param dataList
1663
- * @param styleList
1664
- */
1665
- const createMassMarksLayer = (dataList, styleList = []) => {
1666
- const vectorLayer = new VectorLayer({
1667
- zIndex: 2e3,
1668
- source: new VectorSource({ features: createFeatures(dataList, styleList) })
1669
- });
1670
- vectorLayer.setProperties({
1671
- _isCustom: true,
1672
- _isVectorLayer: true,
1673
- _isMassMarksLayer: true,
1674
- _statusNum: styleList.length / 2,
1675
- _dataList: [...dataList],
1676
- _uuidSet: new Set(dataList.map((item) => item._uuid) || [])
1677
- });
1678
- return vectorLayer;
1679
- };
1680
- /**
1681
- * 添加 massMarksLayer(按需附带线/面图层)
1682
- * @param getImgUrlFn
1683
- */
1684
- const addMassMarksLayer = (getImgUrlFn) => {
1685
- return (dataList = [], iconConfig, openClick = true) => {
1686
- const { offset = [0, 0], baseUrl, hasLine, hasFace, layerName, isDynamic, isAutoFit, statusNum, markerIcon, marksStyles, markerSize, activeScale, vectorConfig } = iconConfig;
1687
- const massMarksStyles = marksStyles ? marksStyles : buildMassMarksStyles(getImgUrlFn)({
1688
- baseUrl,
1689
- markerIcon,
1690
- markerSize,
1691
- statusNum: statusNum || 5,
1692
- activeScale: activeScale || 1.3
1693
- });
1694
- const massMarksLayer = createMassMarksLayer(dataList, massMarksStyles);
1695
- if (openClick) {
1696
- massMarksLayer.setProperties({
1697
- _offset: offset,
1698
- _isAutoFit: isAutoFit
1699
- });
1700
- createSelectByLayer(massMarksLayer);
1701
- }
1702
- if (hasLine) {
1703
- const vectorLayer = createBusinessLayer(dataList, Object.assign({}, {
1704
- key: "line",
1705
- isLine: true,
1706
- openClick: false,
1707
- strokeWeight: 4,
1708
- strokeColor: "rgba(29, 235, 255, 0.8)"
1709
- }, vectorConfig || {}));
1710
- instance.addLayer(vectorLayer);
1711
- }
1712
- if (hasFace) {
1713
- const vectorLayer = createBusinessLayer(dataList, Object.assign({}, {
1714
- isLine: false,
1715
- key: "faceList",
1716
- strokeWeight: 1,
1717
- openClick: false,
1718
- fillColor: "#4247DF",
1719
- strokeStyle: "dashed",
1720
- strokeColor: "#4247DF"
1721
- }, vectorConfig || {}));
1722
- instance.addLayer(vectorLayer);
1723
- }
1724
- massMarksLayer.setProperties({
1725
- _isCustom: true,
1726
- _offset: offset,
1727
- _hasLine: hasLine,
1728
- _hasFace: hasFace,
1729
- className: layerName,
1730
- _layerName: layerName,
1731
- _isVectorLayer: true,
1732
- _isDynamic: isDynamic,
1733
- _massMarksStyles: massMarksStyles
1734
- });
1735
- instance.addLayer(massMarksLayer);
1736
- return massMarksLayer;
1737
- };
1738
- };
1739
- /**
1740
- * 按选中状态列表更新海量点显隐
1741
- * @param checkedStatusList
1742
- */
1743
- const updateMarksLayersByStatus = (checkedStatusList = []) => {
1744
- const hideStyle = new Style({});
1745
- const checkedStatuses = new Set(checkedStatusList);
1746
- const tempList = [];
1747
- instance.getAllLayers().filter((layer) => layer.get("_isMassMarksLayer")).forEach((layer) => {
1748
- layer.getSource().getFeatures().forEach((feature) => {
1749
- const data = feature.getProperties();
1750
- if (!checkedStatuses.has(data.status)) feature.setStyle(hideStyle);
1751
- else {
1752
- tempList.push(data);
1753
- feature.setStyle(data.unActiveStyle);
1754
- }
1755
- });
1756
- });
1757
- return tempList;
1758
- };
1759
-
1760
- //#endregion
1761
- //#region src/layer/cluster-layer.ts
1762
- /**
1763
- * 创建聚合图层(半径/颜色随聚合数量动态计算)
1764
- * @param dataList
1765
- * @param config
1766
- */
1767
- const createClusterLayer = (dataList, config) => {
1768
- const count = dataList.length;
1769
- const styleCache = {};
1770
- const { distance, minDistance, openClick, buildStyle, singleStyles, computedRadius, layerName = "cluster-layer" } = config;
1771
- const innerComputedRadius = (size) => Math.pow(size / count, 1 / 18);
1772
- styleCache[1] = singleStyles;
1773
- const innerBuildStyle = (feature) => {
1774
- const size = feature.get("features").length;
1775
- let style = styleCache[size];
1776
- const tempRadius = computedRadius ? computedRadius(size) : innerComputedRadius(size);
1777
- if (!style) {
1778
- style = new Style({
1779
- image: new CircleStyle({
1780
- radius: tempRadius * 20,
1781
- stroke: new Stroke({
1782
- width: 2,
1783
- color: `hsla(${180 - tempRadius * 180}, 100%, 40%, 1)`
1784
- }),
1785
- fill: new Fill({ color: `hsla(${180 - tempRadius * 180}, 100%, 50%, 1)` })
1786
- }),
1787
- text: new Text({
1788
- text: size.toString(),
1789
- fill: new Fill({ color: `hsla(${180 - tempRadius * 180}, 100%, 10%, 1)` }),
1790
- textBaseline: "middle"
1791
- })
1792
- });
1793
- styleCache[size] = style;
1794
- }
1795
- if (Array.isArray(style)) {
1796
- const index = feature.get("features")[0].get("styleIndex") ?? 0;
1797
- return style[index];
1798
- }
1799
- return style;
1800
- };
1801
- const vectorLayer = new VectorLayer({
1802
- zIndex: 200,
1803
- className: layerName,
1804
- source: new Cluster({
1805
- distance: distance || 80,
1806
- minDistance: minDistance || 0,
1807
- source: new VectorSource({ features: dataList.map((item) => {
1808
- const feature = new Feature(new Point$1(item.lnglat));
1809
- feature.setProperties(item);
1810
- const index = item.style ?? 0;
1811
- feature.setProperties({
1812
- unActiveStyle: singleStyles[index],
1813
- activeStyle: singleStyles[index + singleStyles.length / 2]
1814
- });
1815
- return feature;
1816
- }) })
1817
- }),
1818
- style: (feature) => buildStyle ? buildStyle(feature) : innerBuildStyle(feature)
1819
- });
1820
- vectorLayer.setProperties({
1821
- _isCustom: true,
1822
- _isVectorLayer: true,
1823
- _isClusterLayer: true
1824
- });
1825
- if (openClick) createSelectByLayer(vectorLayer);
1826
- return vectorLayer;
1827
- };
1828
-
1829
- //#endregion
1830
- export { GeometryType, TDT_VECTOR_SOURCE_ID, TDT_XOR_BYTE_INDEXES, TdtDecoderWorkerClient, activeFeatureByUid, addFeaturesByLayer, addMassMarksLayer, appendFeaturesByLayer, baseLayers, buildMassMarksStyles, changeLayer, changeSource, changeUnselectStyle, createAnimationMarker, createBaseLayer, createBusinessLayer, createClusterLayer, createDrawInteraction, createElasticMarkerLayer, createGeoJsonLayer, createIconStyle, createLayerSelectByNames, createMap, createMapInstance, createMapWorldLayer, createMarker, createMarkersLayer, createMassMarksLayer, createMoveMarkerLayer, createOverlay, createSelectByLayer, createTdtVectorSource, createTdtVtsUrl, createText, createVectorFaceLayer, createVectorLineLayer, createWmtsLayer, createZheJiangLayer, decodeMvt, decodeTdtPk, disposeInstance, disposeMap, emitter, encodeStandardMvt, encodeTdtPk, flyToAnimate, getConstantBackgroundColor, getDrawStyle, getLayerByName, getLayersDataList, getMapInstance, instance, loadAndNormalizeTdtVts, loadTdtStyleBundle, mergeTdtStyleLayers, normalizePrivateGeometry, normalizePrivateMvt, privateTypeToStandard, redactTdtToken, removeAllLayers, removeLayerByName, removeOverlays, resetActiveFeature, resetActiveVector, resetMap, resetZoomAndCenter, resolveFeatureProperties, restoreTdtBytes, setFitViewByCoords, setFitViewByGeom, setFitViewByLayer, updateMarksLayersByStatus };
1
+ import{TdtDecoderWorkerClient as e,createTdtVtsUrl as t,redactTdtToken as n}from"tdt-vts-core";import r from"ol/format/MVT.js";import i from"ol/layer/VectorTile.js";import a from"ol/TileState.js";import o from"ol/source/VectorTile.js";import s from"mitt";import{WMTS as c,XYZ as l}from"ol/source.js";import u from"ol/layer/Tile.js";import{GeoJSON as d,WMTSCapabilities as f}from"ol/format.js";import{optionsFromCapabilities as p}from"ol/source/WMTS.js";import{TileGrid as m}from"ol/tilegrid.js";import{Tile as h}from"ol/layer.js";import g from"ol/tilegrid/WMTS.js";import{unByKey as _}from"ol/Observable.js";import v from"ol/layer/Image.js";import ee from"ol/source/Raster.js";import te from"ol/Map.js";import ne from"ol/View.js";import{defaults as re}from"ol/control.js";import y from"gcoord";import{Feature as b,Overlay as ie}from"ol";import{Select as x,defaults as ae}from"ol/interaction.js";import{boundingExtent as oe,getCenter as se}from"ol/extent.js";import{singleClick as ce}from"ol/events/condition.js";import le from"ol/Feature.js";import ue from"ol/geom/Point.js";import S from"ol/layer/Vector.js";import C from"ol/source/Vector.js";import{Circle as de,Fill as w,Icon as T,RegularShape as fe,Stroke as E,Style as D,Text as O}from"ol/style.js";import pe from"ol/interaction/Draw.js";import{getArea as me,getLength as he}from"ol/sphere.js";import ge from"ol/format/GeoJSON.js";import{Point as k}from"ol/geom.js";import _e from"ol/source/Cluster.js";import ve from"ol/style/Circle.js";export*from"tdt-vts-core";let ye=function(e){return e.LineString=`LineString`,e.MultiLineString=`MultiLineString`,e.Polygon=`Polygon`,e.MultiPolygon=`MultiPolygon`,e.Invalid=`Invalid`,e}({});const be=async e=>{if(typeof e!=`string`)return e;if(!e.trim())throw TypeError(`style 地址不能为空`);let t=await fetch(e);if(!t.ok)throw Error(`加载 style 失败:HTTP ${t.status}`);return await t.json()},A=i=>{if(!i.hosts?.length||i.hosts.some(e=>!e.trim()))throw TypeError(`hosts 不能为空`);let s=new e(i.restoreMode,i.decoderOptions),c=new r({layerName:`mvt:layer`}),l=new o({projection:`EPSG:3857`,minZoom:i.minZoom??1,maxZoom:i.maxZoom??18,tileSize:512,wrapX:!0,format:c,attributions:`© <a href="https://www.tianditu.gov.cn/" target="_blank" rel="noopener">天地图</a>`,tileUrlFunction(e){if(!e)return;let[n,r,a]=e;if(n!==void 0&&r!==void 0&&a!==void 0)return t([n,r,a],{token:i.token,hosts:i.hosts,maxZoom:i.maxZoom??18,wrapX:!0})},tileLoadFunction(e,t){let r=e;r.setLoader(async(e,o,l)=>{let u=n(t);try{let n=await s.decode(t),a=c.readFeatures(n.buffer,{extent:e,featureProjection:l});r.setFeatures(a),i.diagnostics?.onDecoded?.(n.stats,u)}catch(e){let t=e instanceof Error?e:Error(String(e));i.diagnostics?.onError?.(t,u),console.error(`天地图私有 VTS 加载失败`,u,t),r.setState(a.ERROR)}})}}),u=l.dispose.bind(l);return l.dispose=()=>{s.dispose(),u()},{source:l,decoder:s}},xe=async e=>{let t=await be(e.style);if(t?.version!==8||!t.sources||!Array.isArray(t.layers))throw TypeError(`style 必须是 Style v8 对象或其 JSON 地址`);let{source:n}=A({token:e.token,hosts:e.hosts,restoreMode:`auto`}),r=new i({declutter:!0,source:n}),a=r.dispose.bind(r);r.dispose=()=>{n.dispose(),a()};try{let{applyBackground:e,applyStyle:n}=await import(`ol-mapbox-style`),i=structuredClone(t);for(let e of i.layers)e.type===`symbol`&&e.layout?.[`text-font`]&&(e.layout[`text-font`]=[`sans-serif`]);return await Promise.all([n(r,i,{projection:`EPSG:4326`,source:`base-tdt-vector-tile`,updateSource:!1}),e(r,i)]),r}catch(e){throw r.dispose(),e}},j=s();let M;const Se=e=>{M=e},Ce=()=>{M=void 0},N=(e,t)=>{if(e.startsWith(`#`)){let n=e.replace(`#`,``);return n.length===3&&(n=n[0]+n[0]+n[1]+n[1]+n[2]+n[2]),`rgba(${parseInt(n.substring(0,2),16)}, ${parseInt(n.substring(2,4),16)}, ${parseInt(n.substring(4,6),16)}, ${t})`}return e},we=()=>M?.getAllLayers().map(e=>e.get(`_dataList`)||[]).flat(2)||[],Te=e=>{let t=e=>!Array.isArray(e)||e.length===0?0:1+t(e[0]),n=e=>{if(!Array.isArray(e)||e.length<3)return!1;let t=e[0],n=e[e.length-1];return t[0]===n[0]&&t[1]===n[1]},r=e=>Array.isArray(e)&&e.length>=2&&typeof e[0]==`number`&&typeof e[1]==`number`,i=e=>Array.isArray(e)&&e.length>=2&&e.every(r),a=e=>Array.isArray(e)?(t(e)===2?[e]:e).every(e=>Array.isArray(e)&&e.length>=4&&n(e)&&e.every(r)):!1,o=t(e);try{return o<2||!e||e.length===0?`Invalid`:o===2&&i(e)?n(e)&&e.length>=4?`Polygon`:`LineString`:o===3&&Array.isArray(e)&&e.every(i)?a(e)?`Polygon`:e.every(e=>!n(e))?`MultiLineString`:`Invalid`:o===4&&Array.isArray(e)&&e.every(e=>a(e))?`MultiPolygon`:`Invalid`}catch(e){return console.error(`Error analyzing coordinates:`,e),`Invalid`}},Ee=new f,P=new Map,De=async e=>{let t=P.get(e);if(t)return t;let n=await fetch(e);if(!n.ok)throw Error(`WMTS capabilities 请求失败:${n.status}`);let r=Ee.read(await n.text());return P.set(e,r),r},Oe=e=>async t=>{let{style:n,format:r,version:i,layerName:a,matrixSet:o}=t,[s,l]=a.split(`:`),d=`${e}/${s}/${l}/gwc/service/wmts?request=GetCapabilities`,f=await De(d),m=p(f,{style:n||``,layer:l,version:i||`1.1.0`,format:r||`image/png`,matrixSet:o||`EPSG:4326`});if(!m)throw Error(`Unable to create TileLayer`);return new u({zIndex:3,className:a,source:new c(m)})},F=`c77eddc667c5bed016fded560baf93e7`,I=(e,t)=>{let n=t.get(`_selectInteraction`);n&&(e.removeInteraction(n),n.dispose(),t.unset(`_selectInteraction`));let r=t.get(`_resolutionChangeKey`);r&&(_(r),t.unset(`_resolutionChangeKey`));let i=t.get(`_dynamicMarkerTimer`);i!==void 0&&(clearTimeout(i),t.unset(`_dynamicMarkerTimer`)),t.getSource()?.dispose(),e.removeLayer(t),t.dispose()},L=(e=`hzsyvector_dark`)=>{let t={zIndex:-5,preload:1,minZoom:8,maxZoom:21,source:new l({tileGrid:new m({tileSize:256,resolutions:[.00549933137253187,.00274966568626595,.00137483284313297,.000687416421566485,.000343708210783242,.000171854105391621,859270526958104e-19,429635263479052e-19,214817631739526e-19,107408815869763e-19,537044079348816e-20,268522039674407e-20,134261019837204e-20],origin:[118.122911693886,31.2869311022836],extent:[116.43263049006,28.6201742126026,123.705594256347,31.9765041681924]}),projection:`EPSG:4326`,urls:[`https://ywtg.citybrain.hangzhou.gov.cn/${e}/{z}/{y}/{x}`,`https://ywtg.citybrain.hangzhou.gov.cn:9456/${e}/{z}/{y}/{x}`,`https://ywtg.citybrain.hangzhou.gov.cn:9457/${e}/{z}/{y}/{x}`,`https://ywtg.citybrain.hangzhou.gov.cn:9458/${e}/{z}/{y}/{x}`,`https://ywtg.citybrain.hangzhou.gov.cn:9459/${e}/{z}/{y}/{x}`]}),properties:{_isBaseLayer:!0,_layerName:e}};return new h(t)},ke=(e,t=F)=>{let[n,r]=e.split(`_`),i={preload:1,zIndex:-10,cacheSize:20240,properties:{_isBaseLayer:!0,_layerName:e},source:new c({crossOrigin:`anonymous`,projection:`EPSG:4326`,wrapX:!0,layer:n,style:`default`,version:`1.0.0`,format:`image/png`,matrixSet:r,tileGrid:new g({tileSize:256,origin:[-180,90],matrixIds:[`1`,`2`,`3`,`4`,`5`,`6`,`7`,`8`,`9`,`10`,`11`,`12`,`13`,`14`,`15`,`16`,`17`,`18`,`19`],resolutions:[.703125,.3515625,.17578125,.087890625,.0439453125,.02197265625,.010986328125,.0054931640625,.00274658203125,.001373291015625,.0006866455078125,.00034332275390625,.000171661376953125,858306884765625e-19,4291534423828125e-20,21457672119140625e-21,10728836059570312e-21,5364418029785156e-21,2682209014892578e-21]}),url:`https://t{0-7}.tianditu.gov.cn/${e}/wmts?tk=${t}`})};return new h(i)},Ae=e=>({baseImgLayer:new h({className:`img-layer`,source:new l({crossOrigin:`anonymous`,tileSize:256,url:`https://t{0-7}.tianditu.gov.cn/DataServer?T=img_w&x={x}&y={y}&l={z}&tk=${e}`}),zIndex:1}),baseCiaLayer:new h({className:`img-layer`,source:new l({crossOrigin:`anonymous`,tileSize:256,url:`https://t{0-7}.tianditu.gov.cn/DataServer?T=cia_w&x={x}&y={y}&l={z}&tk=${e}`}),zIndex:2}),baseVecLayer:new h({className:`vec-layer`,source:new l({crossOrigin:`anonymous`,tileSize:256,url:`https://t{0-7}.tianditu.gov.cn/DataServer?T=vec_w&x={x}&y={y}&l={z}&tk=${e}`}),zIndex:1}),baseCvaLayer:new h({className:`vec-layer`,source:new l({crossOrigin:`anonymous`,tileSize:256,url:`https://t{0-7}.tianditu.gov.cn/DataServer?T=cva_w&x={x}&y={y}&l={z}&tk=${e}`}),zIndex:2})}),je=(e=`emap`,t=`3b0cdd02-c366-4363-b6de-081a9f12b364`)=>new h({preload:1,minZoom:4,maxZoom:20,zIndex:-10,cacheSize:20240,source:new l({wrapX:!0,projection:`EPSG:4326`,crossOrigin:`anonymous`,url:`https://ditu{0-6}.zjzwfw.gov.cn/services/wmts/${e}/default/oss/getTile/{z}/{y}/{x}?token=${t}`})}),Me=e=>M.getAllLayers().find(t=>(t.get(`_layerName`)||t.getClassName())===e),Ne=e=>{let t=Me(e);t&&I(M,t)},Pe=()=>Math.max(1,Math.min(4,globalThis.navigator?.hardwareConcurrency??2)),Fe=(e,t,n,r={})=>{let i=new ee({sources:[e],operationType:`image`,operation:(e,t)=>(((e,t)=>{let n=t.ratioArray,r=t.rgbColorArray;if(n&&r){let[t=0,i=0,a=0]=n;for(let n=0;n<e.length;n+=4){let o=e[n],s=e[n+1],c=e[n+2],l=o*t+s*i+c*a;e[n]=r[0]-l,e[n+1]=r[1]-l,e[n+2]=r[2]-l}}})(e[0].data,t),e[0]),threads:r.threads??Pe()});return i.on(`beforeoperations`,e=>{e.data.ratioArray=n,e.data.rgbColorArray=t}),i},Ie=(e,t,n,r={})=>new v({source:Fe(e,t,n,r)}),R=(e=``)=>{M&&M.getOverlays().getArray().filter(t=>t.get(e||`position`)).forEach(e=>M.removeOverlay(e))},Le=()=>R(`_isAnimationMarker`),Re=(e,t)=>{Le();let{width:n,height:r,lnglat:i,crsForm:a}=t,o=document.createElement(`div`),s=document.createElement(`img`);s.src=e,s.alt=``,n!==void 0&&(s.width=n),r!==void 0&&(s.height=r),o.append(s);let c=new ie({element:o,stopEvent:!1,positioning:`bottom-center`,position:y.transform([+i[0],+i[1]],a||y.WGS84,y.WGS84)});c.set(`_isAnimationMarker`,!0),M.addOverlay(c)},ze=(e={id:`popup-container`,positioning:`top-center`})=>new ie(Object.assign({element:document.getElementById(e.id)||void 0},e)),Be=()=>{R(),M.getAllLayers().forEach(e=>{e.get(`_selectInteraction`)?.getFeatures().clear()})},Ve=()=>{R(),M&&M.getAllLayers().forEach(e=>{e.get(`_isBaseLayer`)||I(M,e)})},He=()=>{R(),M.getAllLayers().filter(e=>e.get(`_isVectorLayer`)).forEach(e=>{let t=e.get(`_selectInteraction`);t&&(t.dispatchEvent({type:`select`,selected:[],mapBrowserEvent:void 0,deselected:t.getFeatures().getArray()}),t.getFeatures().clear())})},Ue=e=>{let t=M.getAllLayers().filter(e=>e.get(`_isVectorLayer`));if(t.length){let n=t.find(t=>t.get(`_uuidSet`).has(e)&&t.get(`_selectInteraction`));if(n){let t=(n.getSource()?.getFeatures()||[]).find(t=>t.get(`_uuid`)===e),r=n.get(`_selectInteraction`);if(r){let n=r.getFeatures().getArray();n.find(t=>t.get(`_uuid`)===e)||(r.dispatchEvent({type:`select`,deselected:n,selected:[t],mapBrowserEvent:void 0}),r.getFeatures().push(t))}}}};let z=[119.71512016971076,30.122490541963536];const We=(e={})=>{if(M)return M;let t=B(e);return Se(t),t},B=(e={})=>{let t=Object.assign({zoom:9.4,maxZoom:18,minZoom:9.4,center:z,baseLayer:`vec-layer`,projection:`EPSG:4326`,layers:[L()],constrainResolution:!1,smoothExtentConstraint:!1,tk:F},e||{}),n=new te({view:new ne(t),controls:re({zoom:!1,rotate:!1,attribution:!1}),interactions:ae({pinchRotate:!1,doubleClickZoom:!1,altShiftDragRotate:!1}),layers:t.layers||[L()]});z=t.center||z,n.activeBaseLayer=t.baseLayer;let r=e=>n.getAllLayers().find(t=>(t.get(`_layerName`)||t.getClassName())===e);n.getLayerByName=r,n.removeLayerByName=e=>{let t=r(e);t&&I(n,t)},n.changeLayersByNames=(e,t)=>{n.getAllLayers().filter(e=>t.includes(e.getClassName())).forEach(t=>t.setVisible(e?.includes(t.getClassName())))};let i,a;return n.on(`pointermove`,e=>{if(a=e.pixel,i!==void 0)return;let t=()=>{i=void 0,a&&(n.getTargetElement().style.cursor=n.hasFeatureAtPixel(a)?`pointer`:``)};i=typeof globalThis.requestAnimationFrame==`function`?globalThis.requestAnimationFrame(t):setTimeout(t,16)}),n.set(`_disposePointerMove`,()=>{i!==void 0&&(typeof globalThis.cancelAnimationFrame==`function`?globalThis.cancelAnimationFrame(i):clearTimeout(i),i=void 0)}),n},V=()=>{if(!M)return;let e=M;e.get(`_disposePointerMove`)?.(),e.unset(`_disposePointerMove`),e.getOverlays().forEach(t=>{e.removeOverlay(t)}),e.getInteractions().clear(),e.getAllLayers().forEach(t=>I(e,t)),e.setTarget(``),e.dispose(),Ce()},Ge=()=>V(),Ke=()=>M,H=(e=z)=>{M&&M.getView().animate({zoom:9.4,center:e,duration:1e3})},qe=(e={center:z,isClearAll:!0})=>{Ve(),H(e.center||z)},U={padding:[50,50,50,50],duration:1e3},Je=(e,t=M,n)=>{let r=t.getView();if(e.length===1)r.animate({zoom:14,duration:1e3,center:e[0],...n||{}});else{let t=oe(e);r.fit(t,{...U,...n||{}})}},Ye=(e,t=M,n)=>{t.getView().fit(e,{...U,...n||{}})},Xe=(e,t=M,n)=>{let r=e.getSource();if(!r||r.isEmpty())return;let i=r.getExtent();i&&t.getView().fit(i,{...U,...n||{}})},Ze=(e,t)=>{let n=M.getView(),r=n.getZoom();n.animate({center:e,duration:2e3}),t?n.animate({zoom:t-2,duration:1e3},{zoom:t,duration:1e3}):n.animate({zoom:r-2,duration:1e3},{zoom:r+2>12?12:r+2,duration:1e3})},W=e=>new O({text:e.text,offsetY:e.offsetY,offsetX:e.offsetX,justify:`center`,textAlign:`center`,font:`16px PingFangSC-Semibold`,fill:new w({color:`rgba(255, 255, 255, 0.8)`})}),G=(e,t=!1)=>new D({image:new T({scale:e.scale||1,rotation:e.rotation||0,anchor:e.anchor||[.5,1],src:t&&e.activeImg||e.img}),text:e.showText?W(e):void 0}),K=(e,t)=>{let n=new le({geometry:new ue([+e.lng,+e.lat]),data:Object.assign({},e||{},{layerName:t.layerName})});return n.setStyle(G(e)),n},q=(e,t)=>{let{layerName:n,visible:r=!0,zIndex:i=10}=t,a=new C;return e.forEach(e=>a.addFeature(K(e,t))),new S({zIndex:i,visible:r,source:a,className:n||`marker-layer`})},J=(e,t)=>{let n=t>=14?`largeImg`:t>=11?`smallImg`:`img`;e.forEach(e=>{let t=e.get(`data`);t.isSelected||e.setStyle(G({...t,img:t[n]},!1))})},Y=(e,t)=>{let n=e.get(`data`),r={...n,isSelected:t};e.set(`data`,r),n.isElastic?J([e],M.getView().getZoom()):e.setStyle(G(r,t))},Qe=e=>{e.forEach(e=>{Y(e,!1)}),e.clear()},$e=(e,t)=>{let n=e.map(e=>({...e,isElastic:!0})),r=q(n,t),i=r.getSource().getFeatures(),a=M.getView();return J(i,a.getZoom()),r.set(`_resolutionChangeKey`,a.on(`change:resolution`,()=>J(i,a.getZoom()))),r},et=(e,t)=>{let n=K(e,t),r=new S({className:t.layerName||`ol-layer`,source:new C({features:[n]})});return M.addLayer(r),{vectorLayer:r}},tt=(e,t)=>{let n=t.getProperties(),{activeStyle:r}=n;if(t.setStyle(r),j.emit(`selected-feature`,n),e.set(`_hasActive`,!0),e.get(`_isDynamic`)){let t=r.getImage(),i=e.get(`_dynamicMarkerTimer`);i!==void 0&&clearTimeout(i);let a=setTimeout(()=>{e.unset(`_dynamicMarkerTimer`),j.emit(`dynamic-marker`,Object.assign({},n.rawData||{},{lnglat:n.lnglat,style:n.style,crsForm:n.crsForm,width:t.getWidth(),height:t.getHeight()}))},1e3);e.set(`_dynamicMarkerTimer`,a)}if(e.get(`_isAutoFit`)){let e=M.getView();e.animate({duration:1e3,zoom:e.getZoom(),center:se(t.getGeometry().getExtent())})}},X=e=>{let t=new x({style:null,multi:!1,hitTolerance:10,layers:[e],condition:t=>{if(!ce(t))return!1;let n=!1;return M.forEachFeatureAtPixel(t.pixel,()=>{n=!0},{layerFilter:t=>t===e}),n}});return t.on(`select`,t=>{let{selected:n,deselected:r}=t;if(n.length){let t=n[0];if(e.get(`_isClusterLayer`)){let n=t.get(`features`);n.length===1&&tt(e,n[0])}else tt(e,t)}if(r.length){let t=r[0],n=t.getProperties();t.setStyle(n.unActiveStyle),e.set(`_hasActive`,!1)}}),M.addInteraction(t),e.set(`_selectInteraction`,t),t},nt=(e,t,n)=>{let r=new x({style:null,layers:t=>e.includes(t.getClassName())});return r.on(`select`,({deselected:e,selected:r})=>{if(e.length){let t=e[0];Y(t,!1),n?.(t)}if(r.length){let e=r[0];Y(e,!0),t?.(e)}}),r},rt=e=>{let t=me(e,{projection:`EPSG:4326`});return t>1e4?`${Math.round(t/1e6*100)/100} km\xB2`:`${Math.round(t*100)/100} m\xB2`},it=e=>{let t=he(e,{projection:`EPSG:4326`});return t>100?`${Math.round(t/1e3*100)/100} km`:`${Math.round(t*100)/100} m`},at=new D({fill:new w({color:`rgba(255, 255, 255, 0.2)`}),stroke:new E({color:`rgba(0, 0, 0, 0.5)`,lineDash:[10,10],width:2}),image:new de({radius:5,stroke:new E({color:`rgba(0, 0, 0, 0.7)`}),fill:new w({color:`rgba(255, 255, 255, 0.2)`})})}),ot=new WeakMap,st=e=>{let t=ot.get(e);if(t)return t;let n=new D({text:new O({font:`14px`,fill:new w({color:`rgba(255, 255, 255, 1)`}),backgroundFill:new w({color:`rgba(0, 0, 0, 0.7)`}),padding:[3,3,3,3],textBaseline:`bottom`,offsetY:-15}),image:new fe({radius:8,points:3,angle:Math.PI,displacement:[0,10],fill:new w({color:`rgba(0, 0, 0, 0.7)`})})});return ot.set(e,n),n},ct=(e,t=at)=>{let n=t?[t]:[],r=e.getGeometry(),i,a;if(r.getType()===`Polygon`?(i=r.getInteriorPoint(),a=rt(r)):r.getType()===`LineString`&&(i=new ue(r.getLastCoordinate()),a=it(r)),i&&a){let t=st(e);t.setGeometry(i),t.getText().setText(a),n.push(t)}return n},lt=(e=`Point`,t,n)=>new pe({source:t.getSource(),type:e,style:n?e=>ct(e,void 0):void 0}),ut=(e,t,n)=>{let{layerName:r}=n||{},i=Object.assign({className:r||`ol-layer`,source:new C({features:new ge().readFeatures(e)}),style:t||new D({stroke:new E({color:`red`,lineDash:[10],width:3}),fill:new w({color:`rgba(0, 0, 255, 0.1)`})})},n||{});return new S(i)},Z=(e,t,n,r)=>t&&n?.get(e[t])||r,dt=(e,t)=>{let{key:n,crsForm:r}=t,i=[];return e.forEach(e=>{try{let t=e[n],r=Array.isArray(t)?t:JSON.parse(t||`[]`);if(r.length){let t=e.geoType||Te(r);t!==`Invalid`&&i.push({type:`Feature`,properties:{...e},geometry:{type:t,coordinates:r}})}}catch(e){console.error(`创建线状数据失败:${e}`)}}),y.transform({type:`FeatureCollection`,features:i},r||y.WGS84,y.WGS84)},ft=(e,t)=>{let{isLine:n,colorMap:r,fillColor:i,colorKey:a,strokeColor:o,fillOpacity:s,strokeWeight:c,activeFillColor:l,activeFillOpacity:u,activeStrokeColor:d,activeStrokeWeight:f}=t||{},p=e.getProperties(),m=new D({stroke:new E({width:f??c??4,color:d||Z(p,a,r,o)})}),h=new D({stroke:new E({width:c??2,color:Z(p,a,r,o)})});return n||(m.setFill(new w({color:N(l||i||``,u??s??1)||N(Z(p,a,r,i)||``,s??1)})),h.setFill(new w({color:N(Z(p,a,r,i)||``,s??1)}))),e.setProperties({activeStyle:m,unActiveStyle:h}),h},Q=(e,t)=>{let n=dt(e,t),r=new d().readFeatures(n),i=r.map(e=>{let t={...e.getProperties()};return delete t.geometry,t});return r.forEach(e=>{e.setStyle(ft(e,t))}),{features:r,acceptedData:i}},$=(e,t)=>{let{isAutoFit:n,layerName:r,openClick:i}=t,{features:a,acceptedData:o}=Q(e,t),s=new S({zIndex:5,className:r||`ol-layer`,source:new C({features:a})});return i&&X(s),s.setProperties({_isCustom:!0,_isAutoFit:n,_isVectorLayer:!0,_vectorConfig:t,_layerName:r,_dataList:o,_uuidSet:new Set(o.map(e=>e._uuid))}),s},pt=(e,t,n)=>{let r=e.getSource(),{_vectorConfig:i}=e.getProperties()||{},{features:a,acceptedData:o}=Q(t,Object.assign({},i,{crsForm:n??i?.crsForm}));r.addFeatures(a);let s=new Set(e.get(`_uuidSet`)||[]);o.forEach(e=>s.add(e._uuid)),e.set(`_uuidSet`,s),e.set(`_dataList`,[...e.get(`_dataList`)||[],...o])},mt=(e=[],t)=>$(e,Object.assign({},t,{isLine:!1})),ht=(e=[],t)=>$(e,Object.assign({},t,{isLine:!0})),gt=e=>t=>{let{baseUrl:n,markerIcon:r,statusNum:i=5,activeScale:a=1.3,markerSize:o=[32,32]}=t,s=e(n),[c,l]=o,u=Array(i*2);for(let e=0;e<i;e++){let t=s(`${r}-${e}`,`.webp`);u[e]=new D({zIndex:88+e,image:new T({width:c,height:l,src:t,anchor:[.5,1]})});let n=s(`${r}-${e}-active`,`.webp`);u[e+i]=new D({zIndex:120,image:new T({width:c*a,height:l*a,anchor:[.5,1],src:n||t})})}return u},_t=(e,t=[])=>e.map(e=>{let{crsForm:n}=e,r=y.transform([+e.lnglat[0],+e.lnglat[1]],n||y.WGS84,y.WGS84),i=new b({geometry:new k(r)});return i.setProperties({...e,unActiveStyle:t[e.style],activeStyle:t[e.style+t.length/2]}),i.setStyle(t[e.style]),i}),vt=(e,t,n={})=>{let r=e.get(`_massMarksStyles`),i=e?.getSource(),{clear:a}=n,o=_t(t,r);a&&i?.clear(),i?.addFeatures(o);let s=[...a?[]:e.get(`_dataList`)||[],...t];e.set(`_dataList`,s),e.set(`_uuidSet`,new Set(s.map(e=>e._uuid)))},yt=(e,t=[])=>{let n=new S({zIndex:2e3,source:new C({features:_t(e,t)})});return n.setProperties({_isCustom:!0,_isVectorLayer:!0,_isMassMarksLayer:!0,_statusNum:t.length/2,_dataList:[...e],_uuidSet:new Set(e.map(e=>e._uuid)||[])}),n},bt=e=>(t=[],n,r=!0)=>{let{offset:i=[0,0],baseUrl:a,hasLine:o,hasFace:s,layerName:c,isDynamic:l,isAutoFit:u,statusNum:d,markerIcon:f,marksStyles:p,markerSize:m,activeScale:h,vectorConfig:g}=n,_=p||gt(e)({baseUrl:a,markerIcon:f,markerSize:m,statusNum:d||5,activeScale:h||1.3}),v=yt(t,_);if(r&&(v.setProperties({_offset:i,_isAutoFit:u}),X(v)),o){let e=$(t,Object.assign({},{key:`line`,isLine:!0,openClick:!1,strokeWeight:4,strokeColor:`rgba(29, 235, 255, 0.8)`},g||{}));M.addLayer(e)}if(s){let e=$(t,Object.assign({},{isLine:!1,key:`faceList`,strokeWeight:1,openClick:!1,fillColor:`#4247DF`,strokeStyle:`dashed`,strokeColor:`#4247DF`},g||{}));M.addLayer(e)}return v.setProperties({_isCustom:!0,_offset:i,_hasLine:o,_hasFace:s,className:c,_layerName:c,_isVectorLayer:!0,_isDynamic:l,_massMarksStyles:_}),M.addLayer(v),v},xt=(e=[])=>{let t=new D({}),n=new Set(e),r=[];return M.getAllLayers().filter(e=>e.get(`_isMassMarksLayer`)).forEach(e=>{e.getSource().getFeatures().forEach(e=>{let i=e.getProperties();n.has(i.status)?(r.push(i),e.setStyle(i.unActiveStyle)):e.setStyle(t)})}),r},St=(e,t)=>{let n=e.length,r={},{distance:i,minDistance:a,openClick:o,buildStyle:s,singleStyles:c,computedRadius:l,layerName:u=`cluster-layer`}=t,d=e=>(e/n)**(1/18);r[1]=c;let f=e=>{let t=e.get(`features`).length,n=r[t],i=l?l(t):d(t);if(n||(n=new D({image:new ve({radius:i*20,stroke:new E({width:2,color:`hsla(${180-i*180}, 100%, 40%, 1)`}),fill:new w({color:`hsla(${180-i*180}, 100%, 50%, 1)`})}),text:new O({text:t.toString(),fill:new w({color:`hsla(${180-i*180}, 100%, 10%, 1)`}),textBaseline:`middle`})}),r[t]=n),Array.isArray(n)){let t=e.get(`features`)[0].get(`styleIndex`)??0;return n[t]}return n},p=new S({zIndex:200,className:u,source:new _e({distance:i||80,minDistance:a||0,source:new C({features:e.map(e=>{let t=new b(new k(e.lnglat));t.setProperties(e);let n=e.style??0;return t.setProperties({unActiveStyle:c[n],activeStyle:c[n+c.length/2]}),t})})}),style:e=>s?s(e):f(e)});return p.setProperties({_isCustom:!0,_isVectorLayer:!0,_isClusterLayer:!0}),o&&X(p),p};export{ye as GeometryType,Ue as activeFeatureByUid,vt as addFeaturesByLayer,bt as addMassMarksLayer,pt as appendFeaturesByLayer,Ae as baseLayers,gt as buildMassMarksStyles,Ie as changeLayer,Fe as changeSource,Qe as changeUnselectStyle,Re as createAnimationMarker,L as createBaseLayer,$ as createBusinessLayer,St as createClusterLayer,lt as createDrawInteraction,$e as createElasticMarkerLayer,ut as createGeoJsonLayer,G as createIconStyle,nt as createLayerSelectByNames,B as createMap,We as createMapInstance,ke as createMapWorldLayer,K as createMarker,q as createMarkersLayer,yt as createMassMarksLayer,et as createMoveMarkerLayer,ze as createOverlay,X as createSelectByLayer,A as createTdtVectorSource,xe as createTdtVtsLayer,W as createText,mt as createVectorFaceLayer,ht as createVectorLineLayer,Oe as createWmtsLayer,je as createZheJiangLayer,Ge as disposeInstance,V as disposeMap,j as emitter,Ze as flyToAnimate,ct as getDrawStyle,Me as getLayerByName,we as getLayersDataList,Ke as getMapInstance,M as instance,Ve as removeAllLayers,Ne as removeLayerByName,R as removeOverlays,He as resetActiveFeature,Be as resetActiveVector,qe as resetMap,H as resetZoomAndCenter,Je as setFitViewByCoords,Ye as setFitViewByGeom,Xe as setFitViewByLayer,xt as updateMarksLayersByStatus};