@caoguo/maplibre 0.0.6 → 0.0.7

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.
Files changed (64) hide show
  1. package/dist/chunk-DENQSIV2.js +81 -0
  2. package/dist/chunk-DENQSIV2.js.map +1 -0
  3. package/dist/chunk-G7VY42ZG.js +210 -0
  4. package/dist/chunk-G7VY42ZG.js.map +1 -0
  5. package/dist/chunk-MLLIAWVI.js +214 -0
  6. package/dist/chunk-MLLIAWVI.js.map +1 -0
  7. package/dist/chunk-OLS4DD3F.js +27 -0
  8. package/dist/chunk-OLS4DD3F.js.map +1 -0
  9. package/dist/chunk-OUEPE6JT.js +26 -0
  10. package/dist/chunk-OUEPE6JT.js.map +1 -0
  11. package/dist/chunk-SKXL3DKL.js +125 -0
  12. package/dist/chunk-SKXL3DKL.js.map +1 -0
  13. package/dist/chunk-Y3JNDOHU.js +56 -0
  14. package/dist/chunk-Y3JNDOHU.js.map +1 -0
  15. package/dist/controls/index.cjs +227 -0
  16. package/dist/controls/index.cjs.map +1 -0
  17. package/dist/controls/index.d.cts +171 -0
  18. package/dist/controls/index.d.ts +171 -0
  19. package/dist/controls/index.js +4 -0
  20. package/dist/controls/index.js.map +1 -0
  21. package/dist/crs/index.cjs +137 -0
  22. package/dist/crs/index.cjs.map +1 -0
  23. package/dist/crs/index.d.cts +80 -0
  24. package/dist/crs/index.d.ts +80 -0
  25. package/dist/crs/index.js +3 -0
  26. package/dist/crs/index.js.map +1 -0
  27. package/dist/index.cjs +48 -13
  28. package/dist/index.cjs.map +1 -1
  29. package/dist/index.d.cts +11 -499
  30. package/dist/index.d.ts +11 -499
  31. package/dist/index.js +14 -643
  32. package/dist/index.js.map +1 -1
  33. package/dist/lod/index.cjs +60 -0
  34. package/dist/lod/index.cjs.map +1 -0
  35. package/dist/lod/index.d.cts +62 -0
  36. package/dist/lod/index.d.ts +62 -0
  37. package/dist/lod/index.js +3 -0
  38. package/dist/lod/index.js.map +1 -0
  39. package/dist/shaders/index.cjs +215 -0
  40. package/dist/shaders/index.cjs.map +1 -0
  41. package/dist/shaders/index.d.cts +131 -0
  42. package/dist/shaders/index.d.ts +131 -0
  43. package/dist/shaders/index.js +3 -0
  44. package/dist/shaders/index.js.map +1 -0
  45. package/dist/sourceUtils.cjs +30 -0
  46. package/dist/sourceUtils.cjs.map +1 -0
  47. package/dist/sourceUtils.d.cts +60 -0
  48. package/dist/sourceUtils.d.ts +60 -0
  49. package/dist/sourceUtils.js +3 -0
  50. package/dist/sourceUtils.js.map +1 -0
  51. package/dist/styles.cjs +47 -12
  52. package/dist/styles.cjs.map +1 -1
  53. package/dist/styles.d.cts +13 -4
  54. package/dist/styles.d.ts +13 -4
  55. package/dist/styles.js +1 -1
  56. package/dist/terrain.cjs +31 -0
  57. package/dist/terrain.cjs.map +1 -0
  58. package/dist/terrain.d.cts +28 -0
  59. package/dist/terrain.d.ts +28 -0
  60. package/dist/terrain.js +3 -0
  61. package/dist/terrain.js.map +1 -0
  62. package/package.json +31 -1
  63. package/dist/chunk-DNEF4DHG.js +0 -48
  64. package/dist/chunk-DNEF4DHG.js.map +0 -1
@@ -0,0 +1,215 @@
1
+ 'use strict';
2
+
3
+ // src/shaders/glowGeometry.ts
4
+ function glowPasses(passes = 4, baseWidth = 3) {
5
+ if (passes < 1) passes = 1;
6
+ const out = [];
7
+ for (let i = 0; i < passes; i++) {
8
+ const t = passes === 1 ? 1 : 1 - i / (passes - 1);
9
+ out.push({
10
+ width: baseWidth * (1 + (1 - t) * (passes - 1) * 1.8),
11
+ opacity: 0.1 + 0.9 * t * t
12
+ });
13
+ }
14
+ return out;
15
+ }
16
+ function projectSimple(lng, lat) {
17
+ const x = lng / 180;
18
+ const y = Math.log(Math.tan(Math.PI / 4 + lat * Math.PI / 360)) / Math.PI;
19
+ return [x, Math.max(-1, Math.min(1, y / Math.PI))];
20
+ }
21
+ function buildGlowGeometry(lines, opts = {}) {
22
+ const passes = glowPasses(opts.passes ?? 4, opts.baseWidth ?? 3);
23
+ const stride = 6;
24
+ const verts = [];
25
+ const passRanges = [];
26
+ const renderGroups = [];
27
+ const groups = [];
28
+ for (const line of lines) {
29
+ const g = line.group ?? "default";
30
+ if (!groups.includes(g)) groups.push(g);
31
+ }
32
+ for (let p = 0; p < passes.length; p++) {
33
+ passes[p];
34
+ const start = verts.length / stride;
35
+ for (const group of groups) {
36
+ const groupStart = verts.length / stride;
37
+ const groupIndex = groups.indexOf(group);
38
+ for (const line of lines) {
39
+ if ((line.group ?? "default") !== group) continue;
40
+ const pts = line.coordinates.map(([lng, lat]) => projectSimple(lng, lat));
41
+ if (pts.length < 2) continue;
42
+ for (let i = 0; i < pts.length - 1; i++) {
43
+ const a = pts[i];
44
+ const b = pts[i + 1];
45
+ const dx = b[0] - a[0];
46
+ const dy = b[1] - a[1];
47
+ const quad = [
48
+ [a[0], a[1], dx, dy, 1, groupIndex],
49
+ [a[0], a[1], dx, dy, -1, groupIndex],
50
+ [b[0], b[1], dx, dy, 1, groupIndex],
51
+ [b[0], b[1], dx, dy, -1, groupIndex]
52
+ ];
53
+ const idx = [0, 1, 2, 2, 1, 3];
54
+ for (const k of idx) {
55
+ const v = quad[k];
56
+ verts.push(v[0], v[1], v[2], v[3], v[4], v[5]);
57
+ }
58
+ }
59
+ }
60
+ const count2 = verts.length / stride - groupStart;
61
+ if (count2 > 0) renderGroups.push({ passIndex: p, group, start: groupStart, count: count2 });
62
+ }
63
+ const count = verts.length / stride - start;
64
+ passRanges.push({ start, count });
65
+ }
66
+ return { vertices: new Float32Array(verts), passRanges, passes, stride, groups, renderGroups };
67
+ }
68
+
69
+ // src/shaders/CustomLineLayer.ts
70
+ var FALLBACK_COLOR = [0.6, 0.7, 0.9];
71
+ var DEFAULT_COLORS = {
72
+ pipe: [0.2, 0.85, 1],
73
+ // 青蓝:管线
74
+ road: [0.6, 0.7, 0.9],
75
+ // 灰蓝:路网
76
+ water: [0.25, 0.55, 0.95]
77
+ // 深蓝:水系
78
+ };
79
+ var VERT = `
80
+ attribute vec2 aPos;
81
+ attribute vec2 aDir;
82
+ attribute float aSide;
83
+ uniform mat4 uMatrix;
84
+ uniform float uWidth;
85
+ uniform vec2 uResolution;
86
+ void main() {
87
+ vec4 clip = uMatrix * vec4(aPos, 0.0, 1.0);
88
+ // \u6BB5\u65B9\u5411\u4E5F\u6295\u5F71\uFF0C\u6C42\u5C4F\u5E55\u7A7A\u95F4\u65B9\u5411
89
+ vec4 clipDir = uMatrix * vec4(aPos + aDir, 0.0, 1.0);
90
+ vec2 s0 = clip.xy / clip.w;
91
+ vec2 s1 = clipDir.xy / clipDir.w;
92
+ vec2 dir = normalize(s1 - s0 + vec2(1e-6));
93
+ vec2 normal = vec2(-dir.y, dir.x);
94
+ vec2 offset = normal * aSide * (uWidth / uResolution * 2.0);
95
+ gl_Position = vec4(s0 + offset * clip.w, clip.z, clip.w);
96
+ }
97
+ `;
98
+ var FRAG = `
99
+ precision mediump float;
100
+ uniform vec3 uColor;
101
+ uniform float uOpacity;
102
+ void main() {
103
+ gl_FragColor = vec4(uColor, uOpacity);
104
+ }
105
+ `;
106
+ function compile(gl, type, src) {
107
+ const sh = gl.createShader(type);
108
+ gl.shaderSource(sh, src);
109
+ gl.compileShader(sh);
110
+ if (!gl.getShaderParameter(sh, gl.COMPILE_STATUS)) {
111
+ console.error("[CustomLineLayer] shader \u7F16\u8BD1\u5931\u8D25:", gl.getShaderInfoLog(sh));
112
+ }
113
+ return sh;
114
+ }
115
+ var CustomLineLayer = class {
116
+ constructor(opts) {
117
+ this.type = "custom";
118
+ this.renderingMode = "2d";
119
+ this.id = opts.id ?? "caoguo-glow-line";
120
+ this.lines = opts.lines;
121
+ this.colors = { ...DEFAULT_COLORS, ...opts.colors };
122
+ this.baseWidth = opts.baseWidth ?? 3;
123
+ this.geometry = buildGlowGeometry(this.lines, { passes: opts.passes ?? 4, baseWidth: this.baseWidth });
124
+ for (const g of this.geometry.groups) {
125
+ if (!(g in this.colors)) this.colors[g] = FALLBACK_COLOR;
126
+ }
127
+ }
128
+ onAdd(map, gl) {
129
+ this.map = map;
130
+ this.gl = gl;
131
+ const vs = compile(gl, gl.VERTEX_SHADER, VERT);
132
+ const fs = compile(gl, gl.FRAGMENT_SHADER, FRAG);
133
+ const prog = gl.createProgram();
134
+ gl.attachShader(prog, vs);
135
+ gl.attachShader(prog, fs);
136
+ gl.linkProgram(prog);
137
+ if (!gl.getProgramParameter(prog, gl.LINK_STATUS)) {
138
+ console.error("[CustomLineLayer] program \u94FE\u63A5\u5931\u8D25:", gl.getProgramInfoLog(prog));
139
+ }
140
+ this.program = prog;
141
+ const buf = gl.createBuffer();
142
+ gl.bindBuffer(gl.ARRAY_BUFFER, buf);
143
+ gl.bufferData(gl.ARRAY_BUFFER, this.geometry.vertices, gl.STATIC_DRAW);
144
+ this.buffer = buf;
145
+ }
146
+ render(gl, matrix) {
147
+ if (!this.program || !this.buffer) return;
148
+ gl.useProgram(this.program);
149
+ gl.bindBuffer(gl.ARRAY_BUFFER, this.buffer);
150
+ const stride = this.geometry.stride * 4;
151
+ const aPos = gl.getAttribLocation(this.program, "aPos");
152
+ const aDir = gl.getAttribLocation(this.program, "aDir");
153
+ const aSide = gl.getAttribLocation(this.program, "aSide");
154
+ const aGroup = gl.getAttribLocation(this.program, "aGroup");
155
+ gl.enableVertexAttribArray(aPos);
156
+ gl.vertexAttribPointer(aPos, 2, gl.FLOAT, false, stride, 0);
157
+ gl.enableVertexAttribArray(aDir);
158
+ gl.vertexAttribPointer(aDir, 2, gl.FLOAT, false, stride, 8);
159
+ gl.enableVertexAttribArray(aSide);
160
+ gl.vertexAttribPointer(aSide, 1, gl.FLOAT, false, stride, 16);
161
+ if (aGroup >= 0) {
162
+ gl.enableVertexAttribArray(aGroup);
163
+ gl.vertexAttribPointer(aGroup, 1, gl.FLOAT, false, stride, 20);
164
+ }
165
+ const uMatrix = gl.getUniformLocation(this.program, "uMatrix");
166
+ gl.uniformMatrix4fv(uMatrix, false, new Float32Array(matrix));
167
+ const uWidth = gl.getUniformLocation(this.program, "uWidth");
168
+ const uColor = gl.getUniformLocation(this.program, "uColor");
169
+ const uOpacity = gl.getUniformLocation(this.program, "uOpacity");
170
+ const uRes = gl.getUniformLocation(this.program, "uResolution");
171
+ const canvas = this.map?.getCanvas();
172
+ gl.uniform2f(uRes, canvas?.width ?? 1024, canvas?.height ?? 768);
173
+ gl.enable(gl.BLEND);
174
+ gl.blendFunc(gl.SRC_ALPHA, gl.ONE);
175
+ for (const rg of this.geometry.renderGroups) {
176
+ const pass = this.geometry.passes[rg.passIndex];
177
+ const color = this.colors[rg.group] ?? FALLBACK_COLOR;
178
+ gl.uniform1f(uWidth, pass.width);
179
+ gl.uniform3f(uColor, color[0], color[1], color[2]);
180
+ gl.uniform1f(uOpacity, pass.opacity);
181
+ gl.drawArrays(gl.TRIANGLES, rg.start, rg.count);
182
+ }
183
+ }
184
+ /**
185
+ * 动态更新线集合(高亮选中、切换数据等无需重建图层)。
186
+ * 浏览器环境会刷新 GPU 缓冲;纯逻辑/测试环境仅更新几何与数据。
187
+ */
188
+ setLines(lines) {
189
+ this.lines = lines;
190
+ this.geometry = buildGlowGeometry(this.lines, {
191
+ passes: this.geometry.passes.length,
192
+ baseWidth: this.baseWidth
193
+ });
194
+ for (const g of this.geometry.groups) {
195
+ if (!(g in this.colors)) this.colors[g] = FALLBACK_COLOR;
196
+ }
197
+ if (this.buffer && this.gl) {
198
+ this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.buffer);
199
+ this.gl.bufferData(this.gl.ARRAY_BUFFER, this.geometry.vertices, this.gl.STATIC_DRAW);
200
+ }
201
+ }
202
+ onRemove(_map, gl) {
203
+ if (this.buffer) gl.deleteBuffer(this.buffer);
204
+ if (this.program) gl.deleteProgram(this.program);
205
+ this.buffer = void 0;
206
+ this.program = void 0;
207
+ }
208
+ };
209
+
210
+ exports.CustomLineLayer = CustomLineLayer;
211
+ exports.buildGlowGeometry = buildGlowGeometry;
212
+ exports.glowPasses = glowPasses;
213
+ exports.projectSimple = projectSimple;
214
+ //# sourceMappingURL=index.cjs.map
215
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/shaders/glowGeometry.ts","../../src/shaders/CustomLineLayer.ts"],"names":["count"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * 辉光线几何构建(T6 / F-1.3 纯逻辑部分)。\n *\n * 把 GeoJSON LineString 集合转换为「多遍描边」所需的三角形带几何。\n * 渲染层(CustomLineLayer)据此在 WebGL 中以屏幕像素宽度的三角面叠加绘制,\n * 每遍一个宽度档 + 透明度档,加法混合形成管线/路网/水系的辉光效果。\n *\n * 设计:本模块**不依赖 WebGL / maplibre**,纯函数可在 Node 单测。\n * 坐标投影使用简化 Web Mercator(经度→x、纬度→y 的归一化世界坐标),\n * 真实渲染时由 CustomLayer 的 matrix 变换到屏幕;线宽在屏幕空间计算。\n */\n\nexport interface GlowLine {\n /** 线坐标 [lng, lat][] */\n coordinates: [number, number][];\n /** 该线所属分组(如 'pipe' | 'road' | 'water'),用于按组分色。缺省归入 'default'。 */\n group?: string;\n}\n\nexport interface GlowPass {\n /** 该遍屏幕像素宽度 */\n width: number;\n /** 该遍不透明度(核心线高、外晕低) */\n opacity: number;\n}\n\nexport interface GlowGeometry {\n /**\n * 扁平化顶点缓冲。每个顶点 6 个 float:\n * [worldX, worldY, dirX, dirY, side, groupIndex]\n * - worldX/worldY:该端点的归一化世界坐标\n * - dirX/dirY:所在线段的世界空间方向(用于屏幕空间法线)\n * - side:±1,左/右扩展符号\n * - groupIndex:该顶点所属分组在 `groups` 中的下标(用于按组分色)\n */\n vertices: Float32Array;\n /** 每遍在 vertices 中的顶点区间 [start, count](按顶点数,非 float 数) */\n passRanges: { start: number; count: number }[];\n /** 每遍参数(从最外晕到核心,由宽到窄) */\n passes: GlowPass[];\n /** 顶点属性跨距(float 数) */\n stride: number;\n /** 参与渲染的去重分组名(与上层 colorMap 对应,下标即 groupIndex) */\n groups: string[];\n /** 逐遍、逐分组的连续顶点区间,供按组分色绘制 */\n renderGroups: { passIndex: number; group: string; start: number; count: number }[];\n}\n\n/**\n * 生成辉光多遍档位。\n * @param passes 遍数(默认 4:3 层外晕 + 1 核心)\n * @param baseWidth 核心线像素宽度(默认 3)\n */\nexport function glowPasses(passes = 4, baseWidth = 3): GlowPass[] {\n if (passes < 1) passes = 1;\n const out: GlowPass[] = [];\n // 从最外晕(宽、淡)到核心(窄、浓)排列\n for (let i = 0; i < passes; i++) {\n const t = passes === 1 ? 1 : 1 - i / (passes - 1); // 1(核心)..0(外晕)\n out.push({\n width: baseWidth * (1 + (1 - t) * (passes - 1) * 1.8),\n opacity: 0.1 + 0.9 * t * t,\n });\n }\n return out;\n}\n\n/**\n * 简化 Web Mercator 归一化(经度→x∈[-1,1],纬度→y 经墨卡托压缩)。\n * 仅用于几何构建与单测,真实投影在渲染层用 map 的 matrix。\n */\nexport function projectSimple(lng: number, lat: number): [number, number] {\n const x = lng / 180;\n const y = Math.log(Math.tan(Math.PI / 4 + (lat * Math.PI) / 360)) / Math.PI;\n // 纬度墨卡托范围约 [-π/2, π/2],归一化到 [-1,1] 方便观察\n return [x, Math.max(-1, Math.min(1, y / Math.PI))];\n}\n\n/**\n * 构建辉光几何:对每条线、每遍、每段生成三角面(2 三角形 = 6 顶点)。\n */\nexport function buildGlowGeometry(\n lines: GlowLine[],\n opts: { passes?: number; baseWidth?: number } = {}\n): GlowGeometry {\n const passes = glowPasses(opts.passes ?? 4, opts.baseWidth ?? 3);\n const stride = 6; // worldX, worldY, dirX, dirY, side, groupIndex\n const verts: number[] = [];\n const passRanges: { start: number; count: number }[] = [];\n const renderGroups: { passIndex: number; group: string; start: number; count: number }[] = [];\n\n // 分组名按首次出现顺序去重,下标即顶点携带的 groupIndex。\n const groups: string[] = [];\n for (const line of lines) {\n const g = line.group ?? 'default';\n if (!groups.includes(g)) groups.push(g);\n }\n\n for (let p = 0; p < passes.length; p++) {\n const pass = passes[p];\n const start = verts.length / stride;\n // 每遍内部按分组聚集成连续区间,便于按组取色绘制。\n for (const group of groups) {\n const groupStart = verts.length / stride;\n const groupIndex = groups.indexOf(group);\n for (const line of lines) {\n if ((line.group ?? 'default') !== group) continue;\n const pts = line.coordinates.map(([lng, lat]) => projectSimple(lng, lat));\n if (pts.length < 2) continue;\n for (let i = 0; i < pts.length - 1; i++) {\n const a = pts[i];\n const b = pts[i + 1];\n // 段方向(世界空间,未归一化,shader 内会归一化)\n const dx = b[0] - a[0];\n const dy = b[1] - a[1];\n // 每个端点左右各一个顶点,组成 quad:aL,aR,bL,bR → (aL,aR,bL),(bL,aR,bR)\n const quad: Array<[number, number, number, number, number, number]> = [\n [a[0], a[1], dx, dy, 1, groupIndex],\n [a[0], a[1], dx, dy, -1, groupIndex],\n [b[0], b[1], dx, dy, 1, groupIndex],\n [b[0], b[1], dx, dy, -1, groupIndex],\n ];\n // 两个三角形:0,1,2 与 2,1,3\n const idx = [0, 1, 2, 2, 1, 3];\n for (const k of idx) {\n const v = quad[k];\n verts.push(v[0], v[1], v[2], v[3], v[4], v[5]);\n }\n }\n }\n const count = verts.length / stride - groupStart;\n if (count > 0) renderGroups.push({ passIndex: p, group, start: groupStart, count });\n }\n const count = verts.length / stride - start;\n passRanges.push({ start, count });\n }\n\n return { vertices: new Float32Array(verts), passRanges, passes, stride, groups, renderGroups };\n}\n","/**\n * 辉光管线 Custom Layer(T6 / F-1.3 WebGL 渲染层)。\n *\n * 实现 MapLibre v4 `CustomLayerInterface`:在地图之上叠加绘制 GeoJSON 线,\n * 用多遍(glowPasses)三角面描边形成管线/路网/水系的辉光效果。\n *\n * 设计:几何构建(投影/三角带)在 `./glowGeometry` 纯函数中完成(可单测);\n * 本文件仅负责 WebGL 状态机与绘制,仅在浏览器执行。\n * 线宽在屏幕空间按像素计算(标准 Mapbox line shader 思路),因此可随缩放保持视觉宽度。\n * 通过 `Map.addGlowLayer` 注入,调用方无需感知 WebGL 细节。\n *\n * 依赖 MapLibre 注入的 `matrix`(map 的 projectionMatrix * modelViewMatrix)。\n */\n\nimport { buildGlowGeometry, type GlowLine } from './glowGeometry';\n\n/** MapLibre v4 CustomLayerInterface 最小子集(避免强类型耦合) */\nexport interface CustomLayerInterface {\n id: string;\n type: 'custom';\n renderingMode?: '2d' | '3d';\n onAdd?(map: unknown, gl: WebGLRenderingContext): void;\n render(gl: WebGLRenderingContext, matrix: number[]): void;\n onRemove?(map: unknown, gl: WebGLRenderingContext): void;\n prerender?(gl: WebGLRenderingContext, matrix: number[]): void;\n}\n\nexport interface GlowLayerOptions {\n /** 图层 id */\n id?: string;\n /** 线分组颜色映射(group -> [r,g,b] 0..1) */\n colors?: Record<string, [number, number, number]>;\n /** 基础线宽(像素,核心线) */\n baseWidth?: number;\n /** 辉光遍数 */\n passes?: number;\n /** 线集合 */\n lines: GlowLine[];\n}\n\n/** 取分组色的兜底色(灰蓝),避免未配置分组时渲染透明/黑。 */\nconst FALLBACK_COLOR: [number, number, number] = [0.6, 0.7, 0.9];\n\nconst DEFAULT_COLORS: Record<string, [number, number, number]> = {\n pipe: [0.2, 0.85, 1.0], // 青蓝:管线\n road: [0.6, 0.7, 0.9], // 灰蓝:路网\n water: [0.25, 0.55, 0.95], // 深蓝:水系\n};\n\n// 顶点属性:aPos(世界xy)、aDir(段方向xy)、aSide(±1)\nconst VERT = `\nattribute vec2 aPos;\nattribute vec2 aDir;\nattribute float aSide;\nuniform mat4 uMatrix;\nuniform float uWidth;\nuniform vec2 uResolution;\nvoid main() {\n vec4 clip = uMatrix * vec4(aPos, 0.0, 1.0);\n // 段方向也投影,求屏幕空间方向\n vec4 clipDir = uMatrix * vec4(aPos + aDir, 0.0, 1.0);\n vec2 s0 = clip.xy / clip.w;\n vec2 s1 = clipDir.xy / clipDir.w;\n vec2 dir = normalize(s1 - s0 + vec2(1e-6));\n vec2 normal = vec2(-dir.y, dir.x);\n vec2 offset = normal * aSide * (uWidth / uResolution * 2.0);\n gl_Position = vec4(s0 + offset * clip.w, clip.z, clip.w);\n}\n`;\n\nconst FRAG = `\nprecision mediump float;\nuniform vec3 uColor;\nuniform float uOpacity;\nvoid main() {\n gl_FragColor = vec4(uColor, uOpacity);\n}\n`;\n\nfunction compile(gl: WebGLRenderingContext, type: number, src: string): WebGLShader {\n const sh = gl.createShader(type)!;\n gl.shaderSource(sh, src);\n gl.compileShader(sh);\n if (!gl.getShaderParameter(sh, gl.COMPILE_STATUS)) {\n console.error('[CustomLineLayer] shader 编译失败:', gl.getShaderInfoLog(sh));\n }\n return sh;\n}\n\nexport class CustomLineLayer implements CustomLayerInterface {\n id: string;\n type = 'custom' as const;\n renderingMode: '2d' = '2d';\n\n private lines: GlowLine[];\n private colors: Record<string, [number, number, number]>;\n private baseWidth: number;\n\n private program?: WebGLProgram;\n private buffer?: WebGLBuffer;\n private geometry: ReturnType<typeof buildGlowGeometry>;\n private map?: { getCanvas: () => HTMLCanvasElement };\n private gl?: WebGLRenderingContext;\n\n constructor(opts: GlowLayerOptions) {\n this.id = opts.id ?? 'caoguo-glow-line';\n this.lines = opts.lines;\n this.colors = { ...DEFAULT_COLORS, ...opts.colors };\n this.baseWidth = opts.baseWidth ?? 3;\n this.geometry = buildGlowGeometry(this.lines, { passes: opts.passes ?? 4, baseWidth: this.baseWidth });\n // 为新分组补充兜底色,保证未显式配置的分组也能渲染。\n for (const g of this.geometry.groups) {\n if (!(g in this.colors)) this.colors[g] = FALLBACK_COLOR;\n }\n }\n\n onAdd(map: unknown, gl: WebGLRenderingContext): void {\n this.map = map as { getCanvas: () => HTMLCanvasElement };\n this.gl = gl;\n const vs = compile(gl, gl.VERTEX_SHADER, VERT);\n const fs = compile(gl, gl.FRAGMENT_SHADER, FRAG);\n const prog = gl.createProgram()!;\n gl.attachShader(prog, vs);\n gl.attachShader(prog, fs);\n gl.linkProgram(prog);\n if (!gl.getProgramParameter(prog, gl.LINK_STATUS)) {\n console.error('[CustomLineLayer] program 链接失败:', gl.getProgramInfoLog(prog));\n }\n this.program = prog;\n\n const buf = gl.createBuffer()!;\n gl.bindBuffer(gl.ARRAY_BUFFER, buf);\n gl.bufferData(gl.ARRAY_BUFFER, this.geometry.vertices, gl.STATIC_DRAW);\n this.buffer = buf;\n }\n\n render(gl: WebGLRenderingContext, matrix: number[]): void {\n if (!this.program || !this.buffer) return;\n gl.useProgram(this.program);\n gl.bindBuffer(gl.ARRAY_BUFFER, this.buffer);\n\n const stride = this.geometry.stride * 4; // float -> byte\n const aPos = gl.getAttribLocation(this.program, 'aPos');\n const aDir = gl.getAttribLocation(this.program, 'aDir');\n const aSide = gl.getAttribLocation(this.program, 'aSide');\n const aGroup = gl.getAttribLocation(this.program, 'aGroup');\n gl.enableVertexAttribArray(aPos);\n gl.vertexAttribPointer(aPos, 2, gl.FLOAT, false, stride, 0);\n gl.enableVertexAttribArray(aDir);\n gl.vertexAttribPointer(aDir, 2, gl.FLOAT, false, stride, 8);\n gl.enableVertexAttribArray(aSide);\n gl.vertexAttribPointer(aSide, 1, gl.FLOAT, false, stride, 16);\n if (aGroup >= 0) {\n gl.enableVertexAttribArray(aGroup);\n gl.vertexAttribPointer(aGroup, 1, gl.FLOAT, false, stride, 20);\n }\n\n const uMatrix = gl.getUniformLocation(this.program, 'uMatrix');\n gl.uniformMatrix4fv(uMatrix, false, new Float32Array(matrix));\n const uWidth = gl.getUniformLocation(this.program, 'uWidth');\n const uColor = gl.getUniformLocation(this.program, 'uColor');\n const uOpacity = gl.getUniformLocation(this.program, 'uOpacity');\n const uRes = gl.getUniformLocation(this.program, 'uResolution');\n const canvas = this.map?.getCanvas();\n gl.uniform2f(uRes, canvas?.width ?? 1024, canvas?.height ?? 768);\n\n gl.enable(gl.BLEND);\n gl.blendFunc(gl.SRC_ALPHA, gl.ONE); // 加法混合形成辉光\n\n // 逐遍、逐分组取色绘制:实现「按分组多色」,不同业务线(管/路/水)各自着色。\n for (const rg of this.geometry.renderGroups) {\n const pass = this.geometry.passes[rg.passIndex];\n const color = this.colors[rg.group] ?? FALLBACK_COLOR;\n gl.uniform1f(uWidth, pass.width);\n gl.uniform3f(uColor, color[0], color[1], color[2]);\n gl.uniform1f(uOpacity, pass.opacity);\n gl.drawArrays(gl.TRIANGLES, rg.start, rg.count);\n }\n }\n\n /**\n * 动态更新线集合(高亮选中、切换数据等无需重建图层)。\n * 浏览器环境会刷新 GPU 缓冲;纯逻辑/测试环境仅更新几何与数据。\n */\n setLines(lines: GlowLine[]): void {\n this.lines = lines;\n this.geometry = buildGlowGeometry(this.lines, {\n passes: this.geometry.passes.length,\n baseWidth: this.baseWidth,\n });\n // 为新分组补充兜底色,保证未显式配置的分组也能渲染。\n for (const g of this.geometry.groups) {\n if (!(g in this.colors)) this.colors[g] = FALLBACK_COLOR;\n }\n // 图层已挂载(this.gl 在 onAdd 时缓存)则刷新 GPU 缓冲。\n if (this.buffer && this.gl) {\n this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.buffer);\n this.gl.bufferData(this.gl.ARRAY_BUFFER, this.geometry.vertices, this.gl.STATIC_DRAW);\n }\n }\n\n onRemove(_map: unknown, gl: WebGLRenderingContext): void {\n if (this.buffer) gl.deleteBuffer(this.buffer);\n if (this.program) gl.deleteProgram(this.program);\n this.buffer = undefined;\n this.program = undefined;\n }\n}\n"]}
@@ -0,0 +1,131 @@
1
+ /**
2
+ * 辉光线几何构建(T6 / F-1.3 纯逻辑部分)。
3
+ *
4
+ * 把 GeoJSON LineString 集合转换为「多遍描边」所需的三角形带几何。
5
+ * 渲染层(CustomLineLayer)据此在 WebGL 中以屏幕像素宽度的三角面叠加绘制,
6
+ * 每遍一个宽度档 + 透明度档,加法混合形成管线/路网/水系的辉光效果。
7
+ *
8
+ * 设计:本模块**不依赖 WebGL / maplibre**,纯函数可在 Node 单测。
9
+ * 坐标投影使用简化 Web Mercator(经度→x、纬度→y 的归一化世界坐标),
10
+ * 真实渲染时由 CustomLayer 的 matrix 变换到屏幕;线宽在屏幕空间计算。
11
+ */
12
+ interface GlowLine {
13
+ /** 线坐标 [lng, lat][] */
14
+ coordinates: [number, number][];
15
+ /** 该线所属分组(如 'pipe' | 'road' | 'water'),用于按组分色。缺省归入 'default'。 */
16
+ group?: string;
17
+ }
18
+ interface GlowPass {
19
+ /** 该遍屏幕像素宽度 */
20
+ width: number;
21
+ /** 该遍不透明度(核心线高、外晕低) */
22
+ opacity: number;
23
+ }
24
+ interface GlowGeometry {
25
+ /**
26
+ * 扁平化顶点缓冲。每个顶点 6 个 float:
27
+ * [worldX, worldY, dirX, dirY, side, groupIndex]
28
+ * - worldX/worldY:该端点的归一化世界坐标
29
+ * - dirX/dirY:所在线段的世界空间方向(用于屏幕空间法线)
30
+ * - side:±1,左/右扩展符号
31
+ * - groupIndex:该顶点所属分组在 `groups` 中的下标(用于按组分色)
32
+ */
33
+ vertices: Float32Array;
34
+ /** 每遍在 vertices 中的顶点区间 [start, count](按顶点数,非 float 数) */
35
+ passRanges: {
36
+ start: number;
37
+ count: number;
38
+ }[];
39
+ /** 每遍参数(从最外晕到核心,由宽到窄) */
40
+ passes: GlowPass[];
41
+ /** 顶点属性跨距(float 数) */
42
+ stride: number;
43
+ /** 参与渲染的去重分组名(与上层 colorMap 对应,下标即 groupIndex) */
44
+ groups: string[];
45
+ /** 逐遍、逐分组的连续顶点区间,供按组分色绘制 */
46
+ renderGroups: {
47
+ passIndex: number;
48
+ group: string;
49
+ start: number;
50
+ count: number;
51
+ }[];
52
+ }
53
+ /**
54
+ * 生成辉光多遍档位。
55
+ * @param passes 遍数(默认 4:3 层外晕 + 1 核心)
56
+ * @param baseWidth 核心线像素宽度(默认 3)
57
+ */
58
+ declare function glowPasses(passes?: number, baseWidth?: number): GlowPass[];
59
+ /**
60
+ * 简化 Web Mercator 归一化(经度→x∈[-1,1],纬度→y 经墨卡托压缩)。
61
+ * 仅用于几何构建与单测,真实投影在渲染层用 map 的 matrix。
62
+ */
63
+ declare function projectSimple(lng: number, lat: number): [number, number];
64
+ /**
65
+ * 构建辉光几何:对每条线、每遍、每段生成三角面(2 三角形 = 6 顶点)。
66
+ */
67
+ declare function buildGlowGeometry(lines: GlowLine[], opts?: {
68
+ passes?: number;
69
+ baseWidth?: number;
70
+ }): GlowGeometry;
71
+
72
+ /**
73
+ * 辉光管线 Custom Layer(T6 / F-1.3 WebGL 渲染层)。
74
+ *
75
+ * 实现 MapLibre v4 `CustomLayerInterface`:在地图之上叠加绘制 GeoJSON 线,
76
+ * 用多遍(glowPasses)三角面描边形成管线/路网/水系的辉光效果。
77
+ *
78
+ * 设计:几何构建(投影/三角带)在 `./glowGeometry` 纯函数中完成(可单测);
79
+ * 本文件仅负责 WebGL 状态机与绘制,仅在浏览器执行。
80
+ * 线宽在屏幕空间按像素计算(标准 Mapbox line shader 思路),因此可随缩放保持视觉宽度。
81
+ * 通过 `Map.addGlowLayer` 注入,调用方无需感知 WebGL 细节。
82
+ *
83
+ * 依赖 MapLibre 注入的 `matrix`(map 的 projectionMatrix * modelViewMatrix)。
84
+ */
85
+
86
+ /** MapLibre v4 CustomLayerInterface 最小子集(避免强类型耦合) */
87
+ interface CustomLayerInterface {
88
+ id: string;
89
+ type: 'custom';
90
+ renderingMode?: '2d' | '3d';
91
+ onAdd?(map: unknown, gl: WebGLRenderingContext): void;
92
+ render(gl: WebGLRenderingContext, matrix: number[]): void;
93
+ onRemove?(map: unknown, gl: WebGLRenderingContext): void;
94
+ prerender?(gl: WebGLRenderingContext, matrix: number[]): void;
95
+ }
96
+ interface GlowLayerOptions {
97
+ /** 图层 id */
98
+ id?: string;
99
+ /** 线分组颜色映射(group -> [r,g,b] 0..1) */
100
+ colors?: Record<string, [number, number, number]>;
101
+ /** 基础线宽(像素,核心线) */
102
+ baseWidth?: number;
103
+ /** 辉光遍数 */
104
+ passes?: number;
105
+ /** 线集合 */
106
+ lines: GlowLine[];
107
+ }
108
+ declare class CustomLineLayer implements CustomLayerInterface {
109
+ id: string;
110
+ type: "custom";
111
+ renderingMode: '2d';
112
+ private lines;
113
+ private colors;
114
+ private baseWidth;
115
+ private program?;
116
+ private buffer?;
117
+ private geometry;
118
+ private map?;
119
+ private gl?;
120
+ constructor(opts: GlowLayerOptions);
121
+ onAdd(map: unknown, gl: WebGLRenderingContext): void;
122
+ render(gl: WebGLRenderingContext, matrix: number[]): void;
123
+ /**
124
+ * 动态更新线集合(高亮选中、切换数据等无需重建图层)。
125
+ * 浏览器环境会刷新 GPU 缓冲;纯逻辑/测试环境仅更新几何与数据。
126
+ */
127
+ setLines(lines: GlowLine[]): void;
128
+ onRemove(_map: unknown, gl: WebGLRenderingContext): void;
129
+ }
130
+
131
+ export { type CustomLayerInterface, CustomLineLayer, type GlowGeometry, type GlowLayerOptions, type GlowLine, type GlowPass, buildGlowGeometry, glowPasses, projectSimple };
@@ -0,0 +1,131 @@
1
+ /**
2
+ * 辉光线几何构建(T6 / F-1.3 纯逻辑部分)。
3
+ *
4
+ * 把 GeoJSON LineString 集合转换为「多遍描边」所需的三角形带几何。
5
+ * 渲染层(CustomLineLayer)据此在 WebGL 中以屏幕像素宽度的三角面叠加绘制,
6
+ * 每遍一个宽度档 + 透明度档,加法混合形成管线/路网/水系的辉光效果。
7
+ *
8
+ * 设计:本模块**不依赖 WebGL / maplibre**,纯函数可在 Node 单测。
9
+ * 坐标投影使用简化 Web Mercator(经度→x、纬度→y 的归一化世界坐标),
10
+ * 真实渲染时由 CustomLayer 的 matrix 变换到屏幕;线宽在屏幕空间计算。
11
+ */
12
+ interface GlowLine {
13
+ /** 线坐标 [lng, lat][] */
14
+ coordinates: [number, number][];
15
+ /** 该线所属分组(如 'pipe' | 'road' | 'water'),用于按组分色。缺省归入 'default'。 */
16
+ group?: string;
17
+ }
18
+ interface GlowPass {
19
+ /** 该遍屏幕像素宽度 */
20
+ width: number;
21
+ /** 该遍不透明度(核心线高、外晕低) */
22
+ opacity: number;
23
+ }
24
+ interface GlowGeometry {
25
+ /**
26
+ * 扁平化顶点缓冲。每个顶点 6 个 float:
27
+ * [worldX, worldY, dirX, dirY, side, groupIndex]
28
+ * - worldX/worldY:该端点的归一化世界坐标
29
+ * - dirX/dirY:所在线段的世界空间方向(用于屏幕空间法线)
30
+ * - side:±1,左/右扩展符号
31
+ * - groupIndex:该顶点所属分组在 `groups` 中的下标(用于按组分色)
32
+ */
33
+ vertices: Float32Array;
34
+ /** 每遍在 vertices 中的顶点区间 [start, count](按顶点数,非 float 数) */
35
+ passRanges: {
36
+ start: number;
37
+ count: number;
38
+ }[];
39
+ /** 每遍参数(从最外晕到核心,由宽到窄) */
40
+ passes: GlowPass[];
41
+ /** 顶点属性跨距(float 数) */
42
+ stride: number;
43
+ /** 参与渲染的去重分组名(与上层 colorMap 对应,下标即 groupIndex) */
44
+ groups: string[];
45
+ /** 逐遍、逐分组的连续顶点区间,供按组分色绘制 */
46
+ renderGroups: {
47
+ passIndex: number;
48
+ group: string;
49
+ start: number;
50
+ count: number;
51
+ }[];
52
+ }
53
+ /**
54
+ * 生成辉光多遍档位。
55
+ * @param passes 遍数(默认 4:3 层外晕 + 1 核心)
56
+ * @param baseWidth 核心线像素宽度(默认 3)
57
+ */
58
+ declare function glowPasses(passes?: number, baseWidth?: number): GlowPass[];
59
+ /**
60
+ * 简化 Web Mercator 归一化(经度→x∈[-1,1],纬度→y 经墨卡托压缩)。
61
+ * 仅用于几何构建与单测,真实投影在渲染层用 map 的 matrix。
62
+ */
63
+ declare function projectSimple(lng: number, lat: number): [number, number];
64
+ /**
65
+ * 构建辉光几何:对每条线、每遍、每段生成三角面(2 三角形 = 6 顶点)。
66
+ */
67
+ declare function buildGlowGeometry(lines: GlowLine[], opts?: {
68
+ passes?: number;
69
+ baseWidth?: number;
70
+ }): GlowGeometry;
71
+
72
+ /**
73
+ * 辉光管线 Custom Layer(T6 / F-1.3 WebGL 渲染层)。
74
+ *
75
+ * 实现 MapLibre v4 `CustomLayerInterface`:在地图之上叠加绘制 GeoJSON 线,
76
+ * 用多遍(glowPasses)三角面描边形成管线/路网/水系的辉光效果。
77
+ *
78
+ * 设计:几何构建(投影/三角带)在 `./glowGeometry` 纯函数中完成(可单测);
79
+ * 本文件仅负责 WebGL 状态机与绘制,仅在浏览器执行。
80
+ * 线宽在屏幕空间按像素计算(标准 Mapbox line shader 思路),因此可随缩放保持视觉宽度。
81
+ * 通过 `Map.addGlowLayer` 注入,调用方无需感知 WebGL 细节。
82
+ *
83
+ * 依赖 MapLibre 注入的 `matrix`(map 的 projectionMatrix * modelViewMatrix)。
84
+ */
85
+
86
+ /** MapLibre v4 CustomLayerInterface 最小子集(避免强类型耦合) */
87
+ interface CustomLayerInterface {
88
+ id: string;
89
+ type: 'custom';
90
+ renderingMode?: '2d' | '3d';
91
+ onAdd?(map: unknown, gl: WebGLRenderingContext): void;
92
+ render(gl: WebGLRenderingContext, matrix: number[]): void;
93
+ onRemove?(map: unknown, gl: WebGLRenderingContext): void;
94
+ prerender?(gl: WebGLRenderingContext, matrix: number[]): void;
95
+ }
96
+ interface GlowLayerOptions {
97
+ /** 图层 id */
98
+ id?: string;
99
+ /** 线分组颜色映射(group -> [r,g,b] 0..1) */
100
+ colors?: Record<string, [number, number, number]>;
101
+ /** 基础线宽(像素,核心线) */
102
+ baseWidth?: number;
103
+ /** 辉光遍数 */
104
+ passes?: number;
105
+ /** 线集合 */
106
+ lines: GlowLine[];
107
+ }
108
+ declare class CustomLineLayer implements CustomLayerInterface {
109
+ id: string;
110
+ type: "custom";
111
+ renderingMode: '2d';
112
+ private lines;
113
+ private colors;
114
+ private baseWidth;
115
+ private program?;
116
+ private buffer?;
117
+ private geometry;
118
+ private map?;
119
+ private gl?;
120
+ constructor(opts: GlowLayerOptions);
121
+ onAdd(map: unknown, gl: WebGLRenderingContext): void;
122
+ render(gl: WebGLRenderingContext, matrix: number[]): void;
123
+ /**
124
+ * 动态更新线集合(高亮选中、切换数据等无需重建图层)。
125
+ * 浏览器环境会刷新 GPU 缓冲;纯逻辑/测试环境仅更新几何与数据。
126
+ */
127
+ setLines(lines: GlowLine[]): void;
128
+ onRemove(_map: unknown, gl: WebGLRenderingContext): void;
129
+ }
130
+
131
+ export { type CustomLayerInterface, CustomLineLayer, type GlowGeometry, type GlowLayerOptions, type GlowLine, type GlowPass, buildGlowGeometry, glowPasses, projectSimple };
@@ -0,0 +1,3 @@
1
+ export { CustomLineLayer, buildGlowGeometry, glowPasses, projectSimple } from '../chunk-G7VY42ZG.js';
2
+ //# sourceMappingURL=index.js.map
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"index.js"}
@@ -0,0 +1,30 @@
1
+ 'use strict';
2
+
3
+ // src/sourceUtils.ts
4
+ function upsertSource(mlMap, id, data) {
5
+ if (mlMap.getSource(id)) {
6
+ if (mlMap.setData) {
7
+ mlMap.setData(id, data);
8
+ } else if (mlMap.addSource) {
9
+ mlMap.addSource(id, data);
10
+ }
11
+ } else if (mlMap.addSource) {
12
+ mlMap.addSource(id, data);
13
+ }
14
+ }
15
+ function removeSourceSafe(mlMap, id) {
16
+ if (mlMap.getSource(id) && mlMap.removeSource) {
17
+ mlMap.removeSource(id);
18
+ }
19
+ }
20
+ function removeSourcesSafe(mlMap, ids) {
21
+ for (const id of ids) {
22
+ removeSourceSafe(mlMap, id);
23
+ }
24
+ }
25
+
26
+ exports.removeSourceSafe = removeSourceSafe;
27
+ exports.removeSourcesSafe = removeSourcesSafe;
28
+ exports.upsertSource = upsertSource;
29
+ //# sourceMappingURL=sourceUtils.cjs.map
30
+ //# sourceMappingURL=sourceUtils.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/sourceUtils.ts"],"names":[],"mappings":";;;AA6CO,SAAS,YAAA,CACd,KAAA,EACA,EAAA,EACA,IAAA,EACM;AACN,EAAA,IAAI,KAAA,CAAM,SAAA,CAAU,EAAE,CAAA,EAAG;AACvB,IAAA,IAAI,MAAM,OAAA,EAAS;AACjB,MAAA,KAAA,CAAM,OAAA,CAAQ,IAAI,IAAI,CAAA;AAAA,IACxB,CAAA,MAAA,IAAW,MAAM,SAAA,EAAW;AAE1B,MAAA,KAAA,CAAM,SAAA,CAAU,IAAI,IAAI,CAAA;AAAA,IAC1B;AAAA,EACF,CAAA,MAAA,IAAW,MAAM,SAAA,EAAW;AAC1B,IAAA,KAAA,CAAM,SAAA,CAAU,IAAI,IAAI,CAAA;AAAA,EAC1B;AACF;AAQO,SAAS,gBAAA,CAAiB,OAAuB,EAAA,EAAkB;AACxE,EAAA,IAAI,KAAA,CAAM,SAAA,CAAU,EAAE,CAAA,IAAK,MAAM,YAAA,EAAc;AAC7C,IAAA,KAAA,CAAM,aAAa,EAAE,CAAA;AAAA,EACvB;AACF;AAQO,SAAS,iBAAA,CAAkB,OAAuB,GAAA,EAAqB;AAC5E,EAAA,KAAA,MAAW,MAAM,GAAA,EAAK;AACpB,IAAA,gBAAA,CAAiB,OAAO,EAAE,CAAA;AAAA,EAC5B;AACF","file":"sourceUtils.cjs","sourcesContent":["/**\n * 原生 MapLibre 地图 source 操作的幂等工具。\n *\n * 各业务 System(grid/water/pipeline/telecom/compute/transport)的 render()\n * 通过 `this.map.instance` 直接调用原生 MapLibre,而非经 CaoguoMap 包装类的\n * 幂等 addSource。为避免在每个 System 内重复书写 `if (!mlMap.getSource(id))`\n * 样板,并根治「层级切换重渲染崩溃」(Source already exists),统一在此提供\n * 幂等 upsert 与安全的 removeSource。\n *\n * 仅依赖原生 MapLibre 的 source 子集接口,便于各 System 直接传入 instance。\n */\n\n/**\n * 原生 MapLibre source 操作的子集接口。\n *\n * 仅 `getSource` 为必填——它是所有 helper 判断「source 是否存在」的统一探针。\n * 其余方法全部可选,因为各业务 System 对 `this.map.instance` 的局部类型断言\n * 只声明了实际用到的若干方法(如 topology 只取 getSource/removeSource,\n * traffic 只取 addSource/addLayer),强制统一必填字段会与这些子集冲突。\n * helper 内部对每个要调用的方法做存在性检查,缺失时安全跳过或回退,\n * 保证类型与运行时都安全(真实 MapLibre 实例始终具备全部方法)。\n */\nexport interface MlMapSourceApi {\n getSource: (id: string) => unknown;\n addSource?: (id: string, source: unknown) => void;\n addLayer?: (layer: unknown) => void;\n setData?: (id: string, data: unknown) => void;\n removeSource?: (id: string) => void;\n}\n\n/**\n * 幂等地确保一个 GeoJSON source 存在并持有给定数据。\n *\n * - 若 source 已存在且具备 setData:setData 更新(避免重复 addSource 抛错)。\n * - 若 source 已存在但无 setData:回退为 addSource(MapLibre 对重复 id 会忽略/覆盖,\n * 不抛错,仍可达成渲染目标)。\n * - 若 source 不存在且具备 addSource:addSource 创建。\n *\n * 注意:若传入对象既无 setData 又无 addSource,则无法创建/更新 source。\n * 各 System 传入的 `this.map.instance` 为真实 MapLibre 实例,始终具备这些方法。\n *\n * @param mlMap 原生 MapLibre 实例(或任意满足 MlMapSourceApi 的对象)\n * @param id source 唯一标识\n * @param data GeoJSON 数据(或 addSource 所需的 source spec)\n */\nexport function upsertSource(\n mlMap: MlMapSourceApi,\n id: string,\n data: unknown,\n): void {\n if (mlMap.getSource(id)) {\n if (mlMap.setData) {\n mlMap.setData(id, data);\n } else if (mlMap.addSource) {\n // 无 setData 时回退为 addSource:MapLibre 对同名 source 会忽略重复创建。\n mlMap.addSource(id, data);\n }\n } else if (mlMap.addSource) {\n mlMap.addSource(id, data);\n }\n}\n\n/**\n * 安全地移除 source:仅当存在且具备 removeSource 时才移除,避免不存在时抛错。\n *\n * @param mlMap 原生 MapLibre 实例\n * @param id source 唯一标识\n */\nexport function removeSourceSafe(mlMap: MlMapSourceApi, id: string): void {\n if (mlMap.getSource(id) && mlMap.removeSource) {\n mlMap.removeSource(id);\n }\n}\n\n/**\n * 安全地批量移除 source。\n *\n * @param mlMap 原生 MapLibre 实例\n * @param ids source 标识数组\n */\nexport function removeSourcesSafe(mlMap: MlMapSourceApi, ids: string[]): void {\n for (const id of ids) {\n removeSourceSafe(mlMap, id);\n }\n}\n"]}
@@ -0,0 +1,60 @@
1
+ /**
2
+ * 原生 MapLibre 地图 source 操作的幂等工具。
3
+ *
4
+ * 各业务 System(grid/water/pipeline/telecom/compute/transport)的 render()
5
+ * 通过 `this.map.instance` 直接调用原生 MapLibre,而非经 CaoguoMap 包装类的
6
+ * 幂等 addSource。为避免在每个 System 内重复书写 `if (!mlMap.getSource(id))`
7
+ * 样板,并根治「层级切换重渲染崩溃」(Source already exists),统一在此提供
8
+ * 幂等 upsert 与安全的 removeSource。
9
+ *
10
+ * 仅依赖原生 MapLibre 的 source 子集接口,便于各 System 直接传入 instance。
11
+ */
12
+ /**
13
+ * 原生 MapLibre source 操作的子集接口。
14
+ *
15
+ * 仅 `getSource` 为必填——它是所有 helper 判断「source 是否存在」的统一探针。
16
+ * 其余方法全部可选,因为各业务 System 对 `this.map.instance` 的局部类型断言
17
+ * 只声明了实际用到的若干方法(如 topology 只取 getSource/removeSource,
18
+ * traffic 只取 addSource/addLayer),强制统一必填字段会与这些子集冲突。
19
+ * helper 内部对每个要调用的方法做存在性检查,缺失时安全跳过或回退,
20
+ * 保证类型与运行时都安全(真实 MapLibre 实例始终具备全部方法)。
21
+ */
22
+ interface MlMapSourceApi {
23
+ getSource: (id: string) => unknown;
24
+ addSource?: (id: string, source: unknown) => void;
25
+ addLayer?: (layer: unknown) => void;
26
+ setData?: (id: string, data: unknown) => void;
27
+ removeSource?: (id: string) => void;
28
+ }
29
+ /**
30
+ * 幂等地确保一个 GeoJSON source 存在并持有给定数据。
31
+ *
32
+ * - 若 source 已存在且具备 setData:setData 更新(避免重复 addSource 抛错)。
33
+ * - 若 source 已存在但无 setData:回退为 addSource(MapLibre 对重复 id 会忽略/覆盖,
34
+ * 不抛错,仍可达成渲染目标)。
35
+ * - 若 source 不存在且具备 addSource:addSource 创建。
36
+ *
37
+ * 注意:若传入对象既无 setData 又无 addSource,则无法创建/更新 source。
38
+ * 各 System 传入的 `this.map.instance` 为真实 MapLibre 实例,始终具备这些方法。
39
+ *
40
+ * @param mlMap 原生 MapLibre 实例(或任意满足 MlMapSourceApi 的对象)
41
+ * @param id source 唯一标识
42
+ * @param data GeoJSON 数据(或 addSource 所需的 source spec)
43
+ */
44
+ declare function upsertSource(mlMap: MlMapSourceApi, id: string, data: unknown): void;
45
+ /**
46
+ * 安全地移除 source:仅当存在且具备 removeSource 时才移除,避免不存在时抛错。
47
+ *
48
+ * @param mlMap 原生 MapLibre 实例
49
+ * @param id source 唯一标识
50
+ */
51
+ declare function removeSourceSafe(mlMap: MlMapSourceApi, id: string): void;
52
+ /**
53
+ * 安全地批量移除 source。
54
+ *
55
+ * @param mlMap 原生 MapLibre 实例
56
+ * @param ids source 标识数组
57
+ */
58
+ declare function removeSourcesSafe(mlMap: MlMapSourceApi, ids: string[]): void;
59
+
60
+ export { type MlMapSourceApi, removeSourceSafe, removeSourcesSafe, upsertSource };