@caoguo/maplibre-pipeline 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/burst/index.cjs +708 -0
- package/dist/burst/index.cjs.map +1 -0
- package/dist/burst/index.d.cts +74 -0
- package/dist/burst/index.d.ts +74 -0
- package/dist/burst/index.js +4 -0
- package/dist/burst/index.js.map +1 -0
- package/dist/burstClass-CGMjcAYy.d.cts +161 -0
- package/dist/burstClass-Cf2p71LE.d.ts +161 -0
- package/dist/chunk-CDEYJ67B.js +80 -0
- package/dist/chunk-CDEYJ67B.js.map +1 -0
- package/dist/chunk-GCDA7KJM.js +289 -0
- package/dist/chunk-GCDA7KJM.js.map +1 -0
- package/dist/chunk-GEJRE2OR.js +351 -0
- package/dist/chunk-GEJRE2OR.js.map +1 -0
- package/dist/chunk-HCT6NSNS.js +532 -0
- package/dist/chunk-HCT6NSNS.js.map +1 -0
- package/dist/chunk-HIRNJVCV.js +13 -0
- package/dist/chunk-HIRNJVCV.js.map +1 -0
- package/dist/chunk-J3AEVXT4.js +164 -0
- package/dist/chunk-J3AEVXT4.js.map +1 -0
- package/dist/chunk-QVY4WJLM.js +256 -0
- package/dist/chunk-QVY4WJLM.js.map +1 -0
- package/dist/chunk-VA2TQWL3.js +340 -0
- package/dist/chunk-VA2TQWL3.js.map +1 -0
- package/dist/chunk-YBLESEN4.js +133 -0
- package/dist/chunk-YBLESEN4.js.map +1 -0
- package/dist/chunk-YS2ICFVK.js +101 -0
- package/dist/chunk-YS2ICFVK.js.map +1 -0
- package/dist/graph/index.cjs +382 -0
- package/dist/graph/index.cjs.map +1 -0
- package/dist/graph/index.d.cts +211 -0
- package/dist/graph/index.d.ts +211 -0
- package/dist/graph/index.js +4 -0
- package/dist/graph/index.js.map +1 -0
- package/dist/health/index.cjs +348 -0
- package/dist/health/index.cjs.map +1 -0
- package/dist/health/index.d.cts +210 -0
- package/dist/health/index.d.ts +210 -0
- package/dist/health/index.js +3 -0
- package/dist/health/index.js.map +1 -0
- package/dist/index.cjs +2295 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +10 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +12 -0
- package/dist/index.js.map +1 -0
- package/dist/leakage/index.cjs +358 -0
- package/dist/leakage/index.cjs.map +1 -0
- package/dist/leakage/index.d.cts +212 -0
- package/dist/leakage/index.d.ts +212 -0
- package/dist/leakage/index.js +3 -0
- package/dist/leakage/index.js.map +1 -0
- package/dist/nlpg/index.cjs +136 -0
- package/dist/nlpg/index.cjs.map +1 -0
- package/dist/nlpg/index.d.cts +51 -0
- package/dist/nlpg/index.d.ts +51 -0
- package/dist/nlpg/index.js +3 -0
- package/dist/nlpg/index.js.map +1 -0
- package/dist/style/index.cjs +379 -0
- package/dist/style/index.cjs.map +1 -0
- package/dist/style/index.d.cts +119 -0
- package/dist/style/index.d.ts +119 -0
- package/dist/style/index.js +4 -0
- package/dist/style/index.js.map +1 -0
- package/dist/topology/index.cjs +317 -0
- package/dist/topology/index.cjs.map +1 -0
- package/dist/topology/index.d.cts +69 -0
- package/dist/topology/index.d.ts +69 -0
- package/dist/topology/index.js +4 -0
- package/dist/topology/index.js.map +1 -0
- package/dist/types/index.cjs +15 -0
- package/dist/types/index.cjs.map +1 -0
- package/dist/types/index.d.cts +214 -0
- package/dist/types/index.d.ts +214 -0
- package/dist/types/index.js +3 -0
- package/dist/types/index.js.map +1 -0
- package/package.json +81 -0
|
@@ -0,0 +1,351 @@
|
|
|
1
|
+
// src/leakage/pasquillGifford.ts
|
|
2
|
+
var TABLE_DAY = [
|
|
3
|
+
{ wind: 2, cls: { strong: "A", moderate: "A", slight: "B" } },
|
|
4
|
+
{ wind: 3, cls: { strong: "A", moderate: "B", slight: "C" } },
|
|
5
|
+
{ wind: 5, cls: { strong: "B", moderate: "B", slight: "C" } },
|
|
6
|
+
{ wind: 6, cls: { strong: "C", moderate: "C", slight: "D" } },
|
|
7
|
+
{ wind: 7, cls: { strong: "C", moderate: "D", slight: "D" } },
|
|
8
|
+
{ wind: 999, cls: { strong: "D", moderate: "D", slight: "D" } }
|
|
9
|
+
];
|
|
10
|
+
var TABLE_NIGHT = [
|
|
11
|
+
{ wind: 2, cls: { overcast: "F", partly: "F", clear: "F" } },
|
|
12
|
+
{ wind: 3, cls: { overcast: "E", partly: "F", clear: "F" } },
|
|
13
|
+
{ wind: 5, cls: { overcast: "D", partly: "E", clear: "F" } },
|
|
14
|
+
{ wind: 6, cls: { overcast: "D", partly: "D", clear: "E" } },
|
|
15
|
+
{ wind: 999, cls: { overcast: "D", partly: "D", clear: "D" } }
|
|
16
|
+
];
|
|
17
|
+
function classifyStability(params) {
|
|
18
|
+
const table = params.isDaytime ? TABLE_DAY : TABLE_NIGHT;
|
|
19
|
+
const key = params.isDaytime ? params.daytimeInsolation ?? "moderate" : params.nightCloudCover ?? "partly";
|
|
20
|
+
for (const row of table) {
|
|
21
|
+
if (params.windSpeed < row.wind) return row.cls[key] ?? "D";
|
|
22
|
+
}
|
|
23
|
+
return "D";
|
|
24
|
+
}
|
|
25
|
+
function dispersionCoefficients(stability) {
|
|
26
|
+
const params = {
|
|
27
|
+
A: [0.22, 1e-4, 0.2, 0],
|
|
28
|
+
B: [0.16, 1e-4, 0.12, 0],
|
|
29
|
+
C: [0.11, 1e-4, 0.08, 2e-4],
|
|
30
|
+
D: [0.08, 1e-4, 0.06, 15e-4],
|
|
31
|
+
E: [0.06, 1e-4, 0.03, 3e-4],
|
|
32
|
+
F: [0.04, 1e-4, 0.016, 3e-4]
|
|
33
|
+
};
|
|
34
|
+
const [a, b, c, d] = params[stability];
|
|
35
|
+
return {
|
|
36
|
+
sigmaY: (xMeters) => {
|
|
37
|
+
const xKm = xMeters / 1e3;
|
|
38
|
+
return a * xKm / Math.sqrt(1 + b * xKm) * 1e3;
|
|
39
|
+
},
|
|
40
|
+
sigmaZ: (xMeters) => {
|
|
41
|
+
const xKm = xMeters / 1e3;
|
|
42
|
+
return c * xKm / Math.sqrt(1 + d * xKm) * 1e3;
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// src/leakage/gaussianPlume.ts
|
|
48
|
+
function gaussianPlume(source, params) {
|
|
49
|
+
const stability = params.stability ?? "D";
|
|
50
|
+
const { sigmaY, sigmaZ } = dispersionCoefficients(stability);
|
|
51
|
+
const thresholds = params.thresholds ?? [1e-3, 5e-3, 0.01];
|
|
52
|
+
const step = params.gridStep ?? 50;
|
|
53
|
+
const range = params.range ?? 5e3;
|
|
54
|
+
const contourList = thresholds.map((t) => ({
|
|
55
|
+
threshold: t,
|
|
56
|
+
polygon: [],
|
|
57
|
+
closed: false
|
|
58
|
+
}));
|
|
59
|
+
const Q = params.leakRate;
|
|
60
|
+
const u = params.windSpeed;
|
|
61
|
+
const H = params.releaseHeight;
|
|
62
|
+
let maxDownwindDistance = 0;
|
|
63
|
+
for (const c of contourList) {
|
|
64
|
+
for (let r = step; r <= range; r += step) {
|
|
65
|
+
const sy = sigmaY(r);
|
|
66
|
+
const sz = sigmaZ(r);
|
|
67
|
+
if (sy <= 0 || sz <= 0) continue;
|
|
68
|
+
const concCenter = Q / (2 * Math.PI * sy * sz * u);
|
|
69
|
+
const zTerm = 2 * Math.exp(-(H * H) / (2 * sz * sz));
|
|
70
|
+
const cMax = concCenter * zTerm;
|
|
71
|
+
if (cMax >= c.threshold) {
|
|
72
|
+
if (r > maxDownwindDistance) maxDownwindDistance = r;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
const poly = [];
|
|
76
|
+
for (let r = step; r <= range; r += step) {
|
|
77
|
+
const sy = sigmaY(r);
|
|
78
|
+
if (sy <= 0) continue;
|
|
79
|
+
const syy = Q / (2 * Math.PI * sy * sigmaZ(r) * u) * 2;
|
|
80
|
+
if (syy < c.threshold) continue;
|
|
81
|
+
const ratio = c.threshold * Math.PI * sy * sigmaZ(r) * u / Q;
|
|
82
|
+
if (ratio >= 1) continue;
|
|
83
|
+
const yMax = sy * Math.sqrt(-2 * Math.log(ratio));
|
|
84
|
+
poly.push([r, yMax]);
|
|
85
|
+
poly.unshift([r, -yMax]);
|
|
86
|
+
}
|
|
87
|
+
c.polygon = polyToLngLat(source, params.windDirection, poly);
|
|
88
|
+
c.closed = poly.length >= 3;
|
|
89
|
+
}
|
|
90
|
+
return {
|
|
91
|
+
source,
|
|
92
|
+
windDirection: params.windDirection,
|
|
93
|
+
windSpeed: params.windSpeed,
|
|
94
|
+
stability,
|
|
95
|
+
contours: contourList,
|
|
96
|
+
maxDownwindDistance
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
function polyToLngLat(source, windDir, poly) {
|
|
100
|
+
return poly.map(([r, y]) => {
|
|
101
|
+
const cosW = Math.cos(windDir);
|
|
102
|
+
const sinW = Math.sin(windDir);
|
|
103
|
+
const dx = r * cosW + y * -sinW;
|
|
104
|
+
const dy = r * sinW + y * cosW;
|
|
105
|
+
const dLat = dy / 110540;
|
|
106
|
+
const dLng = dx / (111320 * Math.cos(source.lat * Math.PI / 180));
|
|
107
|
+
return [source.lng + dLng, source.lat + dLat];
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
function classifyFromMeteo(opts) {
|
|
111
|
+
return classifyStability(opts);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// src/leakage/floodFill.ts
|
|
115
|
+
function simulateFlood(dem, params) {
|
|
116
|
+
const sourceIdx = lngLatToIndex(dem, params.sourceLng, params.sourceLat);
|
|
117
|
+
if (sourceIdx.row < 0 || sourceIdx.row >= dem.rows || sourceIdx.col < 0 || sourceIdx.col >= dem.cols) {
|
|
118
|
+
return {
|
|
119
|
+
source: { lng: params.sourceLng, lat: params.sourceLat },
|
|
120
|
+
floodArea: 0,
|
|
121
|
+
affectedCells: [],
|
|
122
|
+
hull: [],
|
|
123
|
+
maxDepth: 0
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
const sourceElev = dem.elevations[sourceIdx.row * dem.cols + sourceIdx.col] ?? 0;
|
|
127
|
+
(params.duration ?? 30 * 60) / 60;
|
|
128
|
+
params.flowRate ?? 50;
|
|
129
|
+
const initialLevel = params.initialWaterLevel ?? sourceElev + 0.05;
|
|
130
|
+
const visited = /* @__PURE__ */ new Set();
|
|
131
|
+
const affected = [];
|
|
132
|
+
const queue = [];
|
|
133
|
+
queue.push({ row: sourceIdx.row, col: sourceIdx.col, level: initialLevel });
|
|
134
|
+
let maxDepth = 0;
|
|
135
|
+
while (queue.length > 0) {
|
|
136
|
+
const cur = queue.shift();
|
|
137
|
+
const idx = cur.row * dem.cols + cur.col;
|
|
138
|
+
if (visited.has(idx)) continue;
|
|
139
|
+
visited.add(idx);
|
|
140
|
+
if (cur.row < 0 || cur.row >= dem.rows) continue;
|
|
141
|
+
if (cur.col < 0 || cur.col >= dem.cols) continue;
|
|
142
|
+
const elev = dem.elevations[idx] ?? 0;
|
|
143
|
+
const depth = cur.level - elev;
|
|
144
|
+
if (depth <= 0) continue;
|
|
145
|
+
if (depth > maxDepth) maxDepth = depth;
|
|
146
|
+
const lng = dem.west + cur.col * dem.cellSize + dem.cellSize / 2;
|
|
147
|
+
const lat = dem.south + cur.row * dem.cellSize + dem.cellSize / 2;
|
|
148
|
+
affected.push({ lng, lat, elevation: elev, waterDepth: depth });
|
|
149
|
+
const nextLevel = cur.level - 0.01;
|
|
150
|
+
for (const [dr, dc] of [
|
|
151
|
+
[0, 1],
|
|
152
|
+
[0, -1],
|
|
153
|
+
[1, 0],
|
|
154
|
+
[-1, 0]
|
|
155
|
+
]) {
|
|
156
|
+
queue.push({ row: cur.row + dr, col: cur.col + dc, level: nextLevel });
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
const cellArea = dem.cellSize * 111320 * (dem.cellSize * 110540);
|
|
160
|
+
const floodArea = affected.length * cellArea;
|
|
161
|
+
const hull = convexHullLocal(affected.map((c) => [c.lng, c.lat]));
|
|
162
|
+
return {
|
|
163
|
+
source: { lng: params.sourceLng, lat: params.sourceLat },
|
|
164
|
+
floodArea,
|
|
165
|
+
affectedCells: affected,
|
|
166
|
+
hull,
|
|
167
|
+
maxDepth
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
function lngLatToIndex(dem, lng, lat) {
|
|
171
|
+
const col = Math.floor((lng - dem.west) / dem.cellSize);
|
|
172
|
+
const row = Math.floor((lat - dem.south) / dem.cellSize);
|
|
173
|
+
return { row, col };
|
|
174
|
+
}
|
|
175
|
+
function convexHullLocal(points) {
|
|
176
|
+
if (points.length < 3) return [...points];
|
|
177
|
+
const pts = [...points].sort((a, b) => a[0] - b[0] || a[1] - b[1]);
|
|
178
|
+
const cross = (o, a, b) => (a[0] - o[0]) * (b[1] - o[1]) - (a[1] - o[1]) * (b[0] - o[0]);
|
|
179
|
+
const lower = [];
|
|
180
|
+
for (const p of pts) {
|
|
181
|
+
while (lower.length >= 2 && cross(lower[lower.length - 2], lower[lower.length - 1], p) <= 0) lower.pop();
|
|
182
|
+
lower.push(p);
|
|
183
|
+
}
|
|
184
|
+
const upper = [];
|
|
185
|
+
for (let i = pts.length - 1; i >= 0; i--) {
|
|
186
|
+
const p = pts[i];
|
|
187
|
+
while (upper.length >= 2 && cross(upper[upper.length - 2], upper[upper.length - 1], p) <= 0) upper.pop();
|
|
188
|
+
upper.push(p);
|
|
189
|
+
}
|
|
190
|
+
upper.pop();
|
|
191
|
+
lower.pop();
|
|
192
|
+
return lower.concat(upper);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// src/leakage/LeakagePlume.ts
|
|
196
|
+
var LeakagePlume = class {
|
|
197
|
+
map;
|
|
198
|
+
thresholds;
|
|
199
|
+
layerPrefix;
|
|
200
|
+
fillColor;
|
|
201
|
+
layerIds = [];
|
|
202
|
+
listeners = /* @__PURE__ */ new Set();
|
|
203
|
+
lastResult = null;
|
|
204
|
+
constructor(options) {
|
|
205
|
+
this.map = options.map;
|
|
206
|
+
this.thresholds = options.thresholds ?? [1e-3, 5e-3, 0.01];
|
|
207
|
+
this.layerPrefix = options.layerPrefix ?? "cg-leakage";
|
|
208
|
+
this.fillColor = options.fillColor ?? "#fb923c";
|
|
209
|
+
}
|
|
210
|
+
/** 燃气泄漏扩散模拟 */
|
|
211
|
+
simulateGas(source, params) {
|
|
212
|
+
const result = gaussianPlume(source, { ...params, thresholds: this.thresholds });
|
|
213
|
+
this.lastResult = result;
|
|
214
|
+
this.renderGas(result);
|
|
215
|
+
for (const l of this.listeners) l(result);
|
|
216
|
+
return result;
|
|
217
|
+
}
|
|
218
|
+
/** 供水爆管淹没模拟 */
|
|
219
|
+
simulateFlood(dem, params) {
|
|
220
|
+
const result = simulateFlood(dem, params);
|
|
221
|
+
this.lastResult = result;
|
|
222
|
+
this.renderFlood(result);
|
|
223
|
+
for (const l of this.listeners) l(result);
|
|
224
|
+
return result;
|
|
225
|
+
}
|
|
226
|
+
/** 清空图层 */
|
|
227
|
+
clear() {
|
|
228
|
+
for (const id of this.layerIds) {
|
|
229
|
+
this.map.removeLayer(id);
|
|
230
|
+
}
|
|
231
|
+
this.layerIds = [];
|
|
232
|
+
}
|
|
233
|
+
/** 销毁组件 */
|
|
234
|
+
destroy() {
|
|
235
|
+
this.clear();
|
|
236
|
+
this.listeners.clear();
|
|
237
|
+
this.lastResult = null;
|
|
238
|
+
}
|
|
239
|
+
/** 订阅结果 */
|
|
240
|
+
onResult(fn) {
|
|
241
|
+
this.listeners.add(fn);
|
|
242
|
+
return () => this.listeners.delete(fn);
|
|
243
|
+
}
|
|
244
|
+
/** 取最后一次结果 */
|
|
245
|
+
getLastResult() {
|
|
246
|
+
return this.lastResult;
|
|
247
|
+
}
|
|
248
|
+
// --------------------------------------------------
|
|
249
|
+
// 内部渲染(GeoJSON 多边形叠加)
|
|
250
|
+
// --------------------------------------------------
|
|
251
|
+
renderGas(result) {
|
|
252
|
+
this.renderContours(
|
|
253
|
+
result.contours.map((c) => ({
|
|
254
|
+
polygon: c.polygon,
|
|
255
|
+
threshold: c.threshold
|
|
256
|
+
})),
|
|
257
|
+
result.source
|
|
258
|
+
);
|
|
259
|
+
}
|
|
260
|
+
renderFlood(result) {
|
|
261
|
+
if (result.hull.length < 3) return;
|
|
262
|
+
const mlMap = this.map.instance;
|
|
263
|
+
const data = {
|
|
264
|
+
type: "Feature",
|
|
265
|
+
geometry: { type: "Polygon", coordinates: [result.hull] },
|
|
266
|
+
properties: { threshold: 0 }
|
|
267
|
+
};
|
|
268
|
+
const id = `${this.layerPrefix}-flood-fill`;
|
|
269
|
+
if (!mlMap.getSource(id)) mlMap.addSource(id, data);
|
|
270
|
+
try {
|
|
271
|
+
mlMap.addLayer({
|
|
272
|
+
id,
|
|
273
|
+
type: "fill",
|
|
274
|
+
source: id,
|
|
275
|
+
paint: {
|
|
276
|
+
"fill-color": "#3b82f6",
|
|
277
|
+
"fill-opacity": 0.3,
|
|
278
|
+
"fill-outline-color": "#1e40af"
|
|
279
|
+
}
|
|
280
|
+
});
|
|
281
|
+
this.layerIds.push(id);
|
|
282
|
+
} catch {
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
renderContours(contours, source) {
|
|
286
|
+
const mlMap = this.map.instance;
|
|
287
|
+
const data = {
|
|
288
|
+
type: "FeatureCollection",
|
|
289
|
+
features: contours.filter((c) => c.polygon.length >= 3).map((c) => ({
|
|
290
|
+
type: "Feature",
|
|
291
|
+
geometry: { type: "Polygon", coordinates: [c.polygon] },
|
|
292
|
+
properties: { threshold: c.threshold }
|
|
293
|
+
}))
|
|
294
|
+
};
|
|
295
|
+
const sourceId = `${this.layerPrefix}-gas-src`;
|
|
296
|
+
const layerId = `${this.layerPrefix}-gas-fill`;
|
|
297
|
+
if (!mlMap.getSource(sourceId)) mlMap.addSource(sourceId, data);
|
|
298
|
+
try {
|
|
299
|
+
mlMap.addLayer({
|
|
300
|
+
id: layerId,
|
|
301
|
+
type: "fill",
|
|
302
|
+
source: sourceId,
|
|
303
|
+
paint: {
|
|
304
|
+
"fill-color": [
|
|
305
|
+
"step",
|
|
306
|
+
["get", "threshold"],
|
|
307
|
+
this.fillColor,
|
|
308
|
+
5e-3,
|
|
309
|
+
"#f97316",
|
|
310
|
+
0.01,
|
|
311
|
+
"#ef4444"
|
|
312
|
+
],
|
|
313
|
+
"fill-opacity": 0.35,
|
|
314
|
+
"fill-outline-color": this.fillColor
|
|
315
|
+
}
|
|
316
|
+
});
|
|
317
|
+
this.layerIds.push(layerId);
|
|
318
|
+
} catch {
|
|
319
|
+
}
|
|
320
|
+
const ptId = `${this.layerPrefix}-source`;
|
|
321
|
+
mlMap.addSource(ptId, {
|
|
322
|
+
type: "FeatureCollection",
|
|
323
|
+
features: [
|
|
324
|
+
{
|
|
325
|
+
type: "Feature",
|
|
326
|
+
geometry: { type: "Point", coordinates: [source.lng, source.lat] },
|
|
327
|
+
properties: {}
|
|
328
|
+
}
|
|
329
|
+
]
|
|
330
|
+
});
|
|
331
|
+
try {
|
|
332
|
+
mlMap.addLayer({
|
|
333
|
+
id: ptId,
|
|
334
|
+
type: "circle",
|
|
335
|
+
source: ptId,
|
|
336
|
+
paint: {
|
|
337
|
+
"circle-radius": 10,
|
|
338
|
+
"circle-color": "#fbbf24",
|
|
339
|
+
"circle-stroke-width": 3,
|
|
340
|
+
"circle-stroke-color": "#000"
|
|
341
|
+
}
|
|
342
|
+
});
|
|
343
|
+
this.layerIds.push(ptId);
|
|
344
|
+
} catch {
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
};
|
|
348
|
+
|
|
349
|
+
export { LeakagePlume, classifyFromMeteo, classifyStability, dispersionCoefficients, gaussianPlume, simulateFlood };
|
|
350
|
+
//# sourceMappingURL=chunk-GEJRE2OR.js.map
|
|
351
|
+
//# sourceMappingURL=chunk-GEJRE2OR.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/leakage/pasquillGifford.ts","../src/leakage/gaussianPlume.ts","../src/leakage/floodFill.ts","../src/leakage/LeakagePlume.ts"],"names":[],"mappings":"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","file":"chunk-GEJRE2OR.js","sourcesContent":["/**\n * Pasquill-Gifford 大气稳定度参数(PRD §4.3.2)\n *\n * 根据风速 + 日射强度 + 夜间云量,对应稳定度类别 A-F\n * A: 极不稳定,B: 不稳定,C: 弱不稳定,D: 中性,E: 弱稳定,F: 稳定\n *\n * 用于高斯烟羽模型的扩散系数 σy/σz 估计。\n */\n\nexport type StabilityClass = 'A' | 'B' | 'C' | 'D' | 'E' | 'F';\n\nexport interface PasquillGiffordParams {\n /** 风速 m/s */\n windSpeed: number;\n /** 日射强度(白天):强/中等/弱 */\n daytimeInsolation?: 'strong' | 'moderate' | 'slight';\n /** 夜间云量(阴天/部分/晴朗) */\n nightCloudCover?: 'overcast' | 'partly' | 'clear';\n /** 是否白天 */\n isDaytime: boolean;\n}\n\n// 修正后的 Pasquill-Gifford 稳定度分类\n// 参考 EPA 工业源空气污染(ISC)模型常用分类\nconst TABLE_DAY: Array<{ wind: number; cls: Record<string, StabilityClass> }> = [\n { wind: 2, cls: { strong: 'A', moderate: 'A', slight: 'B' } },\n { wind: 3, cls: { strong: 'A', moderate: 'B', slight: 'C' } },\n { wind: 5, cls: { strong: 'B', moderate: 'B', slight: 'C' } },\n { wind: 6, cls: { strong: 'C', moderate: 'C', slight: 'D' } },\n { wind: 7, cls: { strong: 'C', moderate: 'D', slight: 'D' } },\n { wind: 999, cls: { strong: 'D', moderate: 'D', slight: 'D' } },\n];\n\nconst TABLE_NIGHT: Array<{ wind: number; cls: Record<string, StabilityClass> }> = [\n { wind: 2, cls: { overcast: 'F', partly: 'F', clear: 'F' } },\n { wind: 3, cls: { overcast: 'E', partly: 'F', clear: 'F' } },\n { wind: 5, cls: { overcast: 'D', partly: 'E', clear: 'F' } },\n { wind: 6, cls: { overcast: 'D', partly: 'D', clear: 'E' } },\n { wind: 999, cls: { overcast: 'D', partly: 'D', clear: 'D' } },\n];\n\n/**\n * 根据 Pasquill-Gifford 表格,确定稳定度类别\n */\nexport function classifyStability(params: PasquillGiffordParams): StabilityClass {\n const table = params.isDaytime ? TABLE_DAY : TABLE_NIGHT;\n const key = params.isDaytime\n ? (params.daytimeInsolation ?? 'moderate')\n : (params.nightCloudCover ?? 'partly');\n\n for (const row of table) {\n if (params.windSpeed < row.wind) return (row.cls as Record<string, StabilityClass>)[key] ?? 'D';\n }\n return 'D';\n}\n\nexport interface DispersionCoefficients {\n /** σy(m),下风向距离 x 的函数 */\n sigmaY: (x: number) => number;\n /** σz(m),下风向距离 x 的函数 */\n sigmaZ: (x: number) => number;\n}\n\n/**\n * Pasquill-Gifford 扩散系数(Briggs 经验公式)\n * σy, σz = a * x / √(1 + b*x)\n *\n * x 单位 km(外部调用时换算:x (m) / 1000)\n *\n * 表格源自 USEPA ISC3 模型常用设置(单位: m/km 与 1/km)\n */\nexport function dispersionCoefficients(stability: StabilityClass): DispersionCoefficients {\n // [σy 前系数, σy 分母修正系数, σz 前系数, σz 分母修正系数]\n const params: Record<StabilityClass, [number, number, number, number]> = {\n A: [0.22, 0.0001, 0.20, 0.000],\n B: [0.16, 0.0001, 0.12, 0.000],\n C: [0.11, 0.0001, 0.08, 0.0002],\n D: [0.08, 0.0001, 0.06, 0.0015],\n E: [0.06, 0.0001, 0.03, 0.0003],\n F: [0.04, 0.0001, 0.016, 0.0003],\n };\n const [a, b, c, d] = params[stability];\n return {\n sigmaY: (xMeters: number) => {\n const xKm = xMeters / 1000;\n return (a * xKm) / Math.sqrt(1 + b * xKm) * 1000;\n },\n sigmaZ: (xMeters: number) => {\n const xKm = xMeters / 1000;\n return (c * xKm) / Math.sqrt(1 + d * xKm) * 1000;\n },\n };\n}\n","/**\n * 高斯烟羽模型(Gaussian Plume)\n *\n * 用于燃气泄漏扩散模拟(PRD §4.3.2):\n * C(x, y, z) = Q / (2π · σy · σz · u) × exp(-y²/2σy²) ×\n * [exp(-(z-H)²/2σz²) + exp(-(z+H)²/2σz²)]\n *\n * 输入:\n * - sourceCoord(泄漏点):lng/lat\n * - Q:泄漏速率(kg/s)\n * - u:风速(m/s)\n * - windDirection:风向(弧度,0=东)\n * - stability:大气稳定度 A-F\n * - H:泄漏源高度(m)\n * - threshold:警戒浓度(如 20%LEL)\n *\n * 输出:\n * - 等浓度线多边形(不同浓度等级)\n * - 下风向最大影响距离\n */\n\nimport type { DispersionCoefficients } from './pasquillGifford';\nimport { dispersionCoefficients, classifyStability } from './pasquillGifford';\n\nexport interface GasLeakParams {\n /** 风向(弧度,0=东,PI/2=北) */\n windDirection: number;\n /** 风速 m/s */\n windSpeed: number;\n /** 泄漏速率 kg/s */\n leakRate: number;\n /** 释放高度 m */\n releaseHeight: number;\n /** 大气稳定度(可选,不传则自动分类) */\n stability?: 'A' | 'B' | 'C' | 'D' | 'E' | 'F';\n /** 浓度阈值(kg/m³)列表 */\n thresholds?: number[];\n /** 网格步长(m)默认 50 */\n gridStep?: number;\n /** 计算范围(m)默认 5000 */\n range?: number;\n}\n\nexport interface GasLeakResult {\n /** 泄漏点 */\n source: { lng: number; lat: number };\n /** 风向(弧度) */\n windDirection: number;\n /** 风速 m/s */\n windSpeed: number;\n /** 大气稳定度 */\n stability: 'A' | 'B' | 'C' | 'D' | 'E' | 'F';\n /** 等浓度线多边形(阈值对应) */\n contours: ConcentrationContour[];\n /** 下风向最大影响距离(m) */\n maxDownwindDistance: number;\n}\n\nexport interface ConcentrationContour {\n /** 阈值 */\n threshold: number;\n /** 多边形(顺时针 GeoJSON-like) */\n polygon: [number, number][];\n /** 是否闭合 */\n closed: boolean;\n}\n\n/**\n * 计算泄漏源下风向的浓度分布,输出等浓度线多边形\n */\nexport function gaussianPlume(\n source: { lng: number; lat: number },\n params: GasLeakParams\n): GasLeakResult {\n const stability = params.stability ?? 'D';\n const { sigmaY, sigmaZ } = dispersionCoefficients(stability);\n const thresholds = params.thresholds ?? [0.001, 0.005, 0.01];\n const step = params.gridStep ?? 50;\n const range = params.range ?? 5000;\n\n const contourList: ConcentrationContour[] = thresholds.map((t) => ({\n threshold: t,\n polygon: [],\n closed: false,\n }));\n\n // 采样网格:下风向 + 横向\n // 用极坐标(r, theta)→ 转笛卡尔 → lng/lat\n const Q = params.leakRate;\n const u = params.windSpeed;\n const H = params.releaseHeight;\n\n let maxDownwindDistance = 0;\n\n for (const c of contourList) {\n const points: [number, number][] = [];\n // 从近到远扫描 r,找满足 c(r) >= threshold 的最大 r\n for (let r = step; r <= range; r += step) {\n const sy = sigmaY(r);\n const sz = sigmaZ(r);\n if (sy <= 0 || sz <= 0) continue;\n const concCenter = Q / (2 * Math.PI * sy * sz * u);\n const zTerm = 2 * Math.exp(-(H * H) / (2 * sz * sz));\n const cMax = concCenter * zTerm;\n if (cMax >= c.threshold) {\n points.push([r, 0]);\n if (r > maxDownwindDistance) maxDownwindDistance = r;\n }\n }\n\n // 多边形 = 各 r 对应的横向扩展(在每个 r 处 y 范围由 σy 决定)\n const poly: [number, number][] = [];\n for (let r = step; r <= range; r += step) {\n const sy = sigmaY(r);\n if (sy <= 0) continue;\n const syy = Q / (2 * Math.PI * sy * sigmaZ(r) * u) * 2;\n if (syy < c.threshold) continue;\n // 横向浓度 = (Q / 2πσyσzu) × exp(-y²/2σy²) × 2\n // 解出 y:exp(-y²/2σy²) = c.threshold * 2πσyσzu / Q\n // y² = -2σy² ln(...)\n const ratio = (c.threshold * Math.PI * sy * sigmaZ(r) * u) / Q;\n if (ratio >= 1) continue; // 浓度太弱,不存在等值线\n const yMax = sy * Math.sqrt(-2 * Math.log(ratio));\n poly.push([r, yMax]);\n poly.unshift([r, -yMax]);\n }\n c.polygon = polyToLngLat(source, params.windDirection, poly);\n c.closed = poly.length >= 3;\n }\n\n return {\n source,\n windDirection: params.windDirection,\n windSpeed: params.windSpeed,\n stability,\n contours: contourList,\n maxDownwindDistance,\n };\n}\n\n/** 把 (r, y) 极坐标点转换为 (lng, lat) */\nfunction polyToLngLat(\n source: { lng: number; lat: number },\n windDir: number,\n poly: [number, number][]\n): [number, number][] {\n return poly.map(([r, y]) => {\n // 下风向 = windDir 方向;横向 = windDir - π/2(垂直右)\n // 设下风向单位向量 (cosθ, sinθ),横向单位向量 (-sinθ, cosθ)\n // 笛卡尔:dx = r * cosθ + y * (-sinθ), dy = r * sinθ + y * cosθ\n const cosW = Math.cos(windDir);\n const sinW = Math.sin(windDir);\n const dx = r * cosW + y * -sinW;\n const dy = r * sinW + y * cosW;\n\n const dLat = dy / 110540;\n const dLng = dx / (111320 * Math.cos((source.lat * Math.PI) / 180));\n return [source.lng + dLng, source.lat + dLat];\n });\n}\n\n/** 根据气象数据自动确定稳定度类别(便利入口) */\nexport function classifyFromMeteo(opts: {\n windSpeed: number;\n isDaytime: boolean;\n daytimeInsolation?: 'strong' | 'moderate' | 'slight';\n nightCloudCover?: 'overcast' | 'partly' | 'clear';\n}) {\n return classifyStability(opts);\n}\n\nexport { dispersionCoefficients };\nexport type { DispersionCoefficients };\n","/**\n * 供水爆管淹没分析(基于地形的 flood fill)\n *\n * 输入:\n * - DEM:高程网格(lng, lat → elevation)\n * - source:爆管点\n * - 网格分辨率\n *\n * 算法:\n * - 从爆管点出发,flood fill 到相邻低洼地\n * - 估算淹没范围\n */\n\nexport interface DemGrid {\n /** 西南角 lng/lat */\n west: number;\n south: number;\n /** 网格行列数 */\n cols: number;\n rows: number;\n /** 单元尺寸(度) */\n cellSize: number;\n /** 高程数组(row-major,rows × cols) */\n elevations: number[];\n}\n\nexport interface FloodParams {\n sourceLng: number;\n sourceLat: number;\n /** 起始水位(m),默认 0 */\n initialWaterLevel?: number;\n /** 模拟时长(秒),默认 30min */\n duration?: number;\n /** 流量估算(m³/h),默认按中等爆管 50 */\n flowRate?: number;\n}\n\nexport interface FloodResult {\n source: { lng: number; lat: number };\n /** 淹没面积(m²) */\n floodArea: number;\n /** 受影响网格(高程 < 水位) */\n affectedCells: Array<{ lng: number; lat: number; elevation: number; waterDepth: number }>;\n /** 凸包多边形 */\n hull: [number, number][];\n /** 最大水深 */\n maxDepth: number;\n}\n\n/**\n * 简单 flood fill 模拟(按等高线分布)\n */\nexport function simulateFlood(dem: DemGrid, params: FloodParams): FloodResult {\n const t0 = Date.now();\n const sourceIdx = lngLatToIndex(dem, params.sourceLng, params.sourceLat);\n if (sourceIdx.row < 0 || sourceIdx.row >= dem.rows || sourceIdx.col < 0 || sourceIdx.col >= dem.cols) {\n return {\n source: { lng: params.sourceLng, lat: params.sourceLat },\n floodArea: 0,\n affectedCells: [],\n hull: [],\n maxDepth: 0,\n };\n }\n\n const sourceElev = dem.elevations[sourceIdx.row * dem.cols + sourceIdx.col] ?? 0;\n const durationMin = (params.duration ?? 30 * 60) / 60; // s → min\n const flowM3h = params.flowRate ?? 50;\n const volumeM3 = flowM3h * (durationMin / 60); // m³\n const initialLevel = params.initialWaterLevel ?? sourceElev + 0.05;\n\n // 简化:假设水平摊开,直至水量耗尽或边界阻挡\n const visited = new Set<number>();\n const affected: FloodResult['affectedCells'] = [];\n const queue: Array<{ row: number; col: number; level: number }> = [];\n queue.push({ row: sourceIdx.row, col: sourceIdx.col, level: initialLevel });\n\n let maxDepth = 0;\n\n while (queue.length > 0) {\n const cur = queue.shift()!;\n const idx = cur.row * dem.cols + cur.col;\n if (visited.has(idx)) continue;\n visited.add(idx);\n\n if (cur.row < 0 || cur.row >= dem.rows) continue;\n if (cur.col < 0 || cur.col >= dem.cols) continue;\n\n const elev = dem.elevations[idx] ?? 0;\n const depth = cur.level - elev;\n if (depth <= 0) continue;\n if (depth > maxDepth) maxDepth = depth;\n\n const lng = dem.west + cur.col * dem.cellSize + dem.cellSize / 2;\n const lat = dem.south + cur.row * dem.cellSize + dem.cellSize / 2;\n affected.push({ lng, lat, elevation: elev, waterDepth: depth });\n\n // 4 邻居\n const nextLevel = cur.level - 0.01;\n for (const [dr, dc] of [\n [0, 1],\n [0, -1],\n [1, 0],\n [-1, 0],\n ] as Array<[number, number]>) {\n queue.push({ row: cur.row + dr, col: cur.col + dc, level: nextLevel });\n }\n }\n\n const cellArea = (dem.cellSize * 111320) * (dem.cellSize * 110540);\n const floodArea = affected.length * cellArea;\n\n const hull: [number, number][] = convexHullLocal(affected.map((c) => [c.lng, c.lat]));\n\n return {\n source: { lng: params.sourceLng, lat: params.sourceLat },\n floodArea,\n affectedCells: affected,\n hull,\n maxDepth,\n };\n}\n\nfunction lngLatToIndex(dem: DemGrid, lng: number, lat: number): { row: number; col: number } {\n const col = Math.floor((lng - dem.west) / dem.cellSize);\n const row = Math.floor((lat - dem.south) / dem.cellSize);\n return { row, col };\n}\n\nfunction convexHullLocal(points: [number, number][]): [number, number][] {\n if (points.length < 3) return [...points];\n const pts = [...points].sort((a, b) => (a[0] - b[0]) || (a[1] - b[1]));\n const cross = (\n o: [number, number],\n a: [number, number],\n b: [number, number]\n ): number => (a[0] - o[0]) * (b[1] - o[1]) - (a[1] - o[1]) * (b[0] - o[0]);\n const lower: [number, number][] = [];\n for (const p of pts) {\n while (lower.length >= 2 && cross(lower[lower.length - 2], lower[lower.length - 1], p) <= 0) lower.pop();\n lower.push(p);\n }\n const upper: [number, number][] = [];\n for (let i = pts.length - 1; i >= 0; i--) {\n const p = pts[i];\n while (upper.length >= 2 && cross(upper[upper.length - 2], upper[upper.length - 1], p) <= 0) upper.pop();\n upper.push(p);\n }\n upper.pop();\n lower.pop();\n return lower.concat(upper);\n}\n","/**\n * LeakagePlume 组件类(占位 stub)\n *\n * Phase 1 MVP 完整功能待 M6 W1-W2 完成。\n * 当前 stub 仅暴露 API 接口和最小实现。\n */\n\nimport type { Map as CaoguoMap } from '@caoguo/maplibre';\nimport type { GasLeakParams, GasLeakResult } from './gaussianPlume';\nimport { gaussianPlume } from './gaussianPlume';\nimport type { FloodParams, FloodResult } from './floodFill';\nimport { simulateFlood } from './floodFill';\nimport type { DemGrid } from './floodFill';\n\nexport type { GasLeakParams, GasLeakResult, FloodParams, FloodResult, DemGrid };\n\nexport interface LeakagePlumeOptions {\n map: CaoguoMap;\n /** 浓度阈值(kg/m³),可分级 */\n thresholds?: number[];\n /** 层 ID 前缀 */\n layerPrefix?: string;\n /** 区域填充色 */\n fillColor?: string;\n}\n\ntype Listener = (r: GasLeakResult | FloodResult) => void;\n\n/**\n * LeakagePlume 组件(燃气泄漏 / 供水爆管)\n *\n * 用法:\n * const plume = new LeakagePlume({ map });\n * plume.simulateGas({ lng, lat }, { windDirection: 1.5, windSpeed: 3, ... });\n * plume.simulateFlood(demGrid, { sourceLng, sourceLat });\n */\nexport class LeakagePlume {\n private map: CaoguoMap;\n private thresholds: number[];\n private layerPrefix: string;\n private fillColor: string;\n private layerIds: string[] = [];\n private listeners = new Set<Listener>();\n private lastResult: GasLeakResult | FloodResult | null = null;\n\n constructor(options: LeakagePlumeOptions) {\n this.map = options.map;\n this.thresholds = options.thresholds ?? [0.001, 0.005, 0.01];\n this.layerPrefix = options.layerPrefix ?? 'cg-leakage';\n this.fillColor = options.fillColor ?? '#fb923c';\n }\n\n /** 燃气泄漏扩散模拟 */\n simulateGas(\n source: { lng: number; lat: number },\n params: Omit<GasLeakParams, 'thresholds'>\n ): GasLeakResult {\n const result = gaussianPlume(source, { ...params, thresholds: this.thresholds });\n this.lastResult = result;\n this.renderGas(result);\n for (const l of this.listeners) l(result);\n return result;\n }\n\n /** 供水爆管淹没模拟 */\n simulateFlood(dem: DemGrid, params: FloodParams): FloodResult {\n const result = simulateFlood(dem, params);\n this.lastResult = result;\n this.renderFlood(result);\n for (const l of this.listeners) l(result);\n return result;\n }\n\n /** 清空图层 */\n clear(): void {\n for (const id of this.layerIds) {\n this.map.removeLayer(id);\n }\n this.layerIds = [];\n }\n\n /** 销毁组件 */\n destroy(): void {\n this.clear();\n this.listeners.clear();\n this.lastResult = null;\n }\n\n /** 订阅结果 */\n onResult(fn: Listener): () => void {\n this.listeners.add(fn);\n return () => this.listeners.delete(fn);\n }\n\n /** 取最后一次结果 */\n getLastResult(): GasLeakResult | FloodResult | null {\n return this.lastResult;\n }\n\n // --------------------------------------------------\n // 内部渲染(GeoJSON 多边形叠加)\n // --------------------------------------------------\n private renderGas(result: GasLeakResult): void {\n this.renderContours(\n result.contours.map((c) => ({\n polygon: c.polygon,\n threshold: c.threshold,\n })),\n result.source\n );\n }\n\n private renderFlood(result: FloodResult): void {\n if (result.hull.length < 3) return;\n const mlMap = (this.map as unknown as {\n instance: {\n addSource: (id: string, source: unknown) => void;\n addLayer: (layer: unknown) => void;\n getSource: (id: string) => unknown;\n };\n }).instance;\n\n const data: GeoJSON.Feature<GeoJSON.Polygon> = {\n type: 'Feature',\n geometry: { type: 'Polygon', coordinates: [result.hull] },\n properties: { threshold: 0 },\n };\n\n const id = `${this.layerPrefix}-flood-fill`;\n if (!mlMap.getSource(id)) mlMap.addSource(id, data);\n try {\n mlMap.addLayer({\n id,\n type: 'fill',\n source: id,\n paint: {\n 'fill-color': '#3b82f6',\n 'fill-opacity': 0.3,\n 'fill-outline-color': '#1e40af',\n },\n });\n this.layerIds.push(id);\n } catch {\n // ignore\n }\n }\n\n private renderContours(\n contours: Array<{ polygon: [number, number][]; threshold: number }>,\n source: { lng: number; lat: number }\n ): void {\n const mlMap = (this.map as unknown as {\n instance: {\n addSource: (id: string, source: unknown) => void;\n addLayer: (layer: unknown) => void;\n getSource: (id: string) => unknown;\n };\n }).instance;\n\n const data: GeoJSON.FeatureCollection<GeoJSON.Polygon> = {\n type: 'FeatureCollection',\n features: contours\n .filter((c) => c.polygon.length >= 3)\n .map((c) => ({\n type: 'Feature' as const,\n geometry: { type: 'Polygon' as const, coordinates: [c.polygon] },\n properties: { threshold: c.threshold },\n })),\n };\n\n const sourceId = `${this.layerPrefix}-gas-src`;\n const layerId = `${this.layerPrefix}-gas-fill`;\n if (!mlMap.getSource(sourceId)) mlMap.addSource(sourceId, data);\n try {\n mlMap.addLayer({\n id: layerId,\n type: 'fill',\n source: sourceId,\n paint: {\n 'fill-color': [\n 'step',\n ['get', 'threshold'],\n this.fillColor,\n 0.005,\n '#f97316',\n 0.01,\n '#ef4444',\n ] as unknown,\n 'fill-opacity': 0.35,\n 'fill-outline-color': this.fillColor,\n },\n });\n this.layerIds.push(layerId);\n } catch {\n // ignore\n }\n\n // 标记泄漏点\n const ptId = `${this.layerPrefix}-source`;\n mlMap.addSource(ptId, {\n type: 'FeatureCollection',\n features: [\n {\n type: 'Feature',\n geometry: { type: 'Point', coordinates: [source.lng, source.lat] },\n properties: {},\n },\n ],\n });\n try {\n mlMap.addLayer({\n id: ptId,\n type: 'circle',\n source: ptId,\n paint: {\n 'circle-radius': 10,\n 'circle-color': '#fbbf24',\n 'circle-stroke-width': 3,\n 'circle-stroke-color': '#000',\n },\n });\n this.layerIds.push(ptId);\n } catch {\n // ignore\n }\n }\n}\n"]}
|