@caoguo/maplibre-pipeline 0.0.2 → 0.0.4

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 (49) hide show
  1. package/dist/burst/index.cjs +708 -0
  2. package/dist/burst/index.cjs.map +1 -0
  3. package/dist/burst/index.js +4 -0
  4. package/dist/burst/index.js.map +1 -0
  5. package/dist/burstClass-CGMjcAYy.d.cts +161 -0
  6. package/dist/burstClass-Cf2p71LE.d.ts +161 -0
  7. package/dist/chunk-BWQKY35F.js +259 -0
  8. package/dist/chunk-BWQKY35F.js.map +1 -0
  9. package/dist/chunk-CDEYJ67B.js +80 -0
  10. package/dist/chunk-CDEYJ67B.js.map +1 -0
  11. package/dist/chunk-GCDA7KJM.js +289 -0
  12. package/dist/chunk-GCDA7KJM.js.map +1 -0
  13. package/dist/chunk-HCT6NSNS.js +532 -0
  14. package/dist/chunk-HCT6NSNS.js.map +1 -0
  15. package/dist/chunk-HIRNJVCV.js +13 -0
  16. package/dist/chunk-HIRNJVCV.js.map +1 -0
  17. package/dist/chunk-PJMHTPSA.js +352 -0
  18. package/dist/chunk-PJMHTPSA.js.map +1 -0
  19. package/dist/chunk-QVY4WJLM.js +256 -0
  20. package/dist/chunk-QVY4WJLM.js.map +1 -0
  21. package/dist/chunk-VA2TQWL3.js +340 -0
  22. package/dist/chunk-VA2TQWL3.js.map +1 -0
  23. package/dist/chunk-YBLESEN4.js +133 -0
  24. package/dist/chunk-YBLESEN4.js.map +1 -0
  25. package/dist/chunk-YS2ICFVK.js +101 -0
  26. package/dist/chunk-YS2ICFVK.js.map +1 -0
  27. package/dist/graph/index.cjs +382 -0
  28. package/dist/graph/index.cjs.map +1 -0
  29. package/dist/graph/index.js +4 -0
  30. package/dist/graph/index.js.map +1 -0
  31. package/dist/health/index.cjs +348 -0
  32. package/dist/health/index.cjs.map +1 -0
  33. package/dist/health/index.js +3 -0
  34. package/dist/health/index.js.map +1 -0
  35. package/dist/index.cjs +2389 -0
  36. package/dist/index.cjs.map +1 -0
  37. package/dist/index.d.cts +10 -0
  38. package/dist/index.d.ts +10 -0
  39. package/dist/index.js +12 -0
  40. package/dist/index.js.map +1 -0
  41. package/dist/leakage/index.cjs +7 -6
  42. package/dist/leakage/index.cjs.map +1 -1
  43. package/dist/leakage/index.js +1 -1
  44. package/dist/topology/index.cjs +113 -14
  45. package/dist/topology/index.cjs.map +1 -1
  46. package/dist/topology/index.d.cts +65 -15
  47. package/dist/topology/index.d.ts +65 -15
  48. package/dist/topology/index.js +1 -1
  49. package/package.json +3 -3
@@ -0,0 +1,348 @@
1
+ 'use strict';
2
+
3
+ // src/health/healthScorer.ts
4
+ var DEFAULT_WEIGHTS = {
5
+ age: 0.25,
6
+ material: 0.2,
7
+ soil: 0.15,
8
+ history: 0.2,
9
+ pressure: 0.1,
10
+ protection: 0.1
11
+ };
12
+ var MATERIAL_RISK = {
13
+ cast_iron: 0.85,
14
+ // 铸铁 = 高风险(旧管网)
15
+ ductile_iron: 0.2,
16
+ // 球墨铸铁
17
+ steel: 0.4,
18
+ pe: 0.1,
19
+ pvc: 0.15,
20
+ concrete: 0.3,
21
+ hdpe: 0.05,
22
+ copper: 0.1,
23
+ unknown: 0.5
24
+ };
25
+ function scorePipeHealth(input, weights = DEFAULT_WEIGHTS) {
26
+ const dims = {
27
+ age: scoreAge(input, weights.age),
28
+ material: scoreMaterial(input, weights.material),
29
+ soil: scoreSoil(input, weights.soil),
30
+ history: scoreHistory(input, weights.history),
31
+ pressure: scorePressure(input, weights.pressure),
32
+ protection: scoreProtection(input, weights.protection)
33
+ };
34
+ const total = dims.age.score * dims.age.weight + dims.material.score * dims.material.weight + dims.soil.score * dims.soil.weight + dims.history.score * dims.history.weight + dims.pressure.score * dims.pressure.weight + dims.protection.score * dims.protection.weight;
35
+ let final = total;
36
+ if (input.status === "damaged") final *= 0.5;
37
+ else if (input.status === "under_repair") final *= 0.7;
38
+ else if (input.status === "abandoned") final = 0;
39
+ return {
40
+ score: Math.round(final),
41
+ level: toLevel(final),
42
+ dimensions: dims
43
+ };
44
+ }
45
+ function toLevel(score) {
46
+ if (score >= 80) return "excellent";
47
+ if (score >= 60) return "good";
48
+ if (score >= 40) return "fair";
49
+ if (score >= 20) return "poor";
50
+ return "critical";
51
+ }
52
+ function scoreAge(input, weight) {
53
+ if (!input.installDate) {
54
+ return { score: 50, weight, reason: "\u672A\u77E5\u5B89\u88C5\u65E5\u671F\uFF0C\u6309\u4E2D\u7B49\u8BA1" };
55
+ }
56
+ const ageYears = (Date.now() - new Date(input.installDate).getTime()) / (365.25 * 24 * 3600 * 1e3);
57
+ if (ageYears < 0) return { score: 100, weight, reason: "\u672A\u6765\u65E5\u671F\uFF0C\u89C6\u4E3A\u4F18\u79C0" };
58
+ if (ageYears < 5) return { score: 100, weight, reason: "\u65B0\u5EFA\u7BA1\u7EBF (<5\u5E74)" };
59
+ if (ageYears < 15) return { score: 85, weight, reason: "\u8F83\u65B0 (5-15\u5E74)" };
60
+ if (ageYears < 25) return { score: 65, weight, reason: "\u4E2D\u9F84 (15-25\u5E74)" };
61
+ if (ageYears < 40) return { score: 40, weight, reason: "\u8001\u9F84 (25-40\u5E74)" };
62
+ if (ageYears < 60) return { score: 20, weight, reason: "\u8D85\u671F (>40\u5E74)" };
63
+ return { score: 5, weight, reason: "\u8D85\u671F (>60\u5E74)" };
64
+ }
65
+ function scoreMaterial(input, weight) {
66
+ if (!input.material) {
67
+ return { score: 50, weight, reason: "\u672A\u77E5\u6750\u8D28\uFF0C\u6309\u4E2D\u7B49\u8BA1" };
68
+ }
69
+ const risk = MATERIAL_RISK[input.material] ?? 0.5;
70
+ const score = Math.round((1 - risk) * 100);
71
+ const labels = {
72
+ cast_iron: "\u94F8\u94C1\uFF08\u9AD8\u98CE\u9669\uFF09",
73
+ ductile_iron: "\u7403\u58A8\u94F8\u94C1",
74
+ steel: "\u94A2",
75
+ pe: "PE\uFF08\u4F4E\u98CE\u9669\uFF09",
76
+ pvc: "PVC",
77
+ concrete: "\u6DF7\u51DD\u571F",
78
+ hdpe: "HDPE\uFF08\u6700\u4F73\uFF09",
79
+ copper: "\u94DC",
80
+ unknown: "\u672A\u77E5"
81
+ };
82
+ return {
83
+ score,
84
+ weight,
85
+ reason: labels[input.material] ?? input.material
86
+ };
87
+ }
88
+ function scoreSoil(input, weight) {
89
+ const c = input.soilCorrosion ?? 0.5;
90
+ const score = Math.round((1 - c) * 100);
91
+ return {
92
+ score,
93
+ weight,
94
+ reason: c === 0.5 && !input.soilCorrosion ? "\u65E0\u571F\u58E4\u6570\u636E\uFF0C\u6309\u5E73\u5747\u8BA1" : `\u571F\u58E4\u8150\u8680\u6307\u6570 ${c.toFixed(2)}`
95
+ };
96
+ }
97
+ function scoreHistory(input, weight) {
98
+ const fail = input.failureCount ?? 0;
99
+ if (fail === 0) return { score: 100, weight, reason: "\u5386\u53F2\u65E0\u6545\u969C" };
100
+ if (fail === 1) return { score: 75, weight, reason: "\u5386\u53F2 1 \u6B21\u6545\u969C" };
101
+ if (fail === 2) return { score: 50, weight, reason: "\u5386\u53F2 2 \u6B21\u6545\u969C" };
102
+ if (fail === 3) return { score: 25, weight, reason: "\u5386\u53F2 3 \u6B21\u6545\u969C" };
103
+ return { score: 0, weight, reason: `\u5386\u53F2 \u2265${fail} \u6B21\u6545\u969C` };
104
+ }
105
+ function scorePressure(input, weight) {
106
+ if (!input.pressure) {
107
+ return { score: 75, weight, reason: "\u65E0\u538B\u529B\u6570\u636E\uFF0C\u6309\u4E2D\u7B49\u504F\u597D\u8BA1" };
108
+ }
109
+ if (!input.ratedPressure) {
110
+ return { score: 75, weight, reason: "\u4EC5\u77E5\u5F53\u524D\u538B\u529B" };
111
+ }
112
+ const ratio = input.pressure / input.ratedPressure;
113
+ if (ratio < 0.5) return { score: 60, weight, reason: "\u538B\u529B\u504F\u4F4E (\u4F7F\u7528\u7387 <50%)" };
114
+ if (ratio < 0.8) return { score: 90, weight, reason: "\u538B\u529B\u6B63\u5E38 (\u4F7F\u7528\u7387 50-80%)" };
115
+ if (ratio < 1) return { score: 75, weight, reason: "\u538B\u529B\u504F\u9AD8 (\u4F7F\u7528\u7387 80-100%)" };
116
+ if (ratio < 1.2) return { score: 30, weight, reason: "\u538B\u529B\u8D85\u9650 (100-120%)" };
117
+ return { score: 0, weight, reason: "\u538B\u529B\u4E25\u91CD\u8D85\u9650 (>120%)" };
118
+ }
119
+ function scoreProtection(input, weight) {
120
+ if (input.hasCathodicProtection === true) {
121
+ return { score: 90, weight, reason: "\u6709\u9634\u6781\u4FDD\u62A4" };
122
+ }
123
+ if (input.hasCathodicProtection === false) {
124
+ return { score: 30, weight, reason: "\u65E0\u9634\u6781\u4FDD\u62A4" };
125
+ }
126
+ return { score: 50, weight, reason: "\u9634\u4FDD\u72B6\u6001\u672A\u77E5" };
127
+ }
128
+ function scorePipes(inputs, weights = DEFAULT_WEIGHTS) {
129
+ return inputs.map((i) => scorePipeHealth(i, weights));
130
+ }
131
+
132
+ // src/health/riskHeatmap.ts
133
+ function aggregateHeatmap(points, options = {}) {
134
+ const cell = options.cellSize ?? 500;
135
+ const agg = options.aggregation ?? "average";
136
+ if (points.length === 0) return [];
137
+ const refLat = points[0].lat;
138
+ const refLng = points[0].lng;
139
+ const mPerDegLat = 110540;
140
+ const mPerDegLng = 111320 * Math.cos(refLat * Math.PI / 180);
141
+ const grid = /* @__PURE__ */ new Map();
142
+ for (const p of points) {
143
+ const x = (p.lng - refLng) * mPerDegLng;
144
+ const y = (p.lat - refLat) * mPerDegLat;
145
+ const cx = Math.floor(x / cell);
146
+ const cy = Math.floor(y / cell);
147
+ const key = `${cx}:${cy}`;
148
+ const cur = grid.get(key);
149
+ if (!cur) {
150
+ grid.set(key, {
151
+ acc: p.healthScore,
152
+ count: 1,
153
+ minS: p.healthScore,
154
+ maxS: p.healthScore,
155
+ lng: p.lng,
156
+ lat: p.lat
157
+ });
158
+ } else {
159
+ cur.acc += p.healthScore;
160
+ cur.count += 1;
161
+ if (p.healthScore < cur.minS) cur.minS = p.healthScore;
162
+ if (p.healthScore > cur.maxS) cur.maxS = p.healthScore;
163
+ cur.lng = (cur.lng * (cur.count - 1) + p.lng) / cur.count;
164
+ cur.lat = (cur.lat * (cur.count - 1) + p.lat) / cur.count;
165
+ }
166
+ }
167
+ const cells = [];
168
+ for (const v of grid.values()) {
169
+ let score;
170
+ if (agg === "min") score = v.minS;
171
+ else if (agg === "max") score = v.maxS;
172
+ else score = v.acc / v.count;
173
+ cells.push({
174
+ lng: v.lng,
175
+ lat: v.lat,
176
+ healthScore: Math.round(score),
177
+ pipeCount: v.count
178
+ });
179
+ }
180
+ return cells;
181
+ }
182
+ function heatmapToGeoJSON(cells) {
183
+ return {
184
+ type: "FeatureCollection",
185
+ features: cells.map((c) => ({
186
+ type: "Feature",
187
+ geometry: { type: "Point", coordinates: [c.lng, c.lat] },
188
+ properties: {
189
+ healthScore: c.healthScore,
190
+ pipeCount: c.pipeCount
191
+ }
192
+ }))
193
+ };
194
+ }
195
+ function prioritizeMaintenance(pipes, topN = 10) {
196
+ return [...pipes].sort((a, b) => a.healthScore - b.healthScore).slice(0, topN);
197
+ }
198
+
199
+ // src/health/PipelineHealth.ts
200
+ var PipelineHealth = class {
201
+ map;
202
+ dataset;
203
+ cellSize;
204
+ layerPrefix;
205
+ listeners = /* @__PURE__ */ new Set();
206
+ lastResult = null;
207
+ layerIds = [];
208
+ constructor(options) {
209
+ this.map = options.map;
210
+ this.dataset = options.dataset;
211
+ this.cellSize = options.cellSize ?? 500;
212
+ this.layerPrefix = options.layerPrefix ?? "cg-health";
213
+ }
214
+ /** 评估全网管线健康度 */
215
+ evaluate(weights = DEFAULT_WEIGHTS) {
216
+ const t0 = typeof performance !== "undefined" ? performance.now() : Date.now();
217
+ const nodeById = new Map(this.dataset.nodes.map((n) => [n.id, n]));
218
+ const scores = this.dataset.pipes.map((p) => {
219
+ const input = {
220
+ installDate: p.properties?.installDate,
221
+ material: p.properties?.material,
222
+ failureCount: void 0,
223
+ pressure: p.properties?.pressure,
224
+ ratedPressure: p.properties?.ratedPressure,
225
+ hasCathodicProtection: void 0,
226
+ status: p.properties?.status
227
+ };
228
+ return { pipeId: p.id, score: scorePipeHealth(input, weights) };
229
+ });
230
+ const points = scores.flatMap((s, i) => {
231
+ const pipe = this.dataset.pipes[i];
232
+ const from = nodeById.get(pipe.fromNode);
233
+ const to = nodeById.get(pipe.toNode);
234
+ if (!from || !to) return [];
235
+ return [
236
+ {
237
+ lng: (from.lng + to.lng) / 2,
238
+ lat: (from.lat + to.lat) / 2,
239
+ healthScore: s.score.score
240
+ }
241
+ ];
242
+ });
243
+ const heatmap = aggregateHeatmap(points, { cellSize: this.cellSize });
244
+ const maintenance = prioritizeMaintenance(
245
+ scores.flatMap((s, i) => {
246
+ const pipe = this.dataset.pipes[i];
247
+ const from = nodeById.get(pipe.fromNode);
248
+ const to = nodeById.get(pipe.toNode);
249
+ if (!from || !to) return [];
250
+ return [
251
+ {
252
+ id: pipe.id,
253
+ healthScore: s.score.score,
254
+ lng: (from.lng + to.lng) / 2,
255
+ lat: (from.lat + to.lat) / 2,
256
+ label: pipe.id
257
+ }
258
+ ];
259
+ })
260
+ );
261
+ const durationMs = (typeof performance !== "undefined" ? performance.now() : Date.now()) - t0;
262
+ const result = { scores, heatmap, maintenance, durationMs };
263
+ this.lastResult = result;
264
+ this.renderHeatmap(result);
265
+ for (const l of this.listeners) l(result);
266
+ return result;
267
+ }
268
+ /** 清空图层 */
269
+ clear() {
270
+ for (const id of this.layerIds) {
271
+ this.map.removeLayer(id);
272
+ }
273
+ this.layerIds = [];
274
+ }
275
+ /** 销毁 */
276
+ destroy() {
277
+ this.clear();
278
+ this.listeners.clear();
279
+ this.lastResult = null;
280
+ }
281
+ /** 订阅结果 */
282
+ onResult(fn) {
283
+ this.listeners.add(fn);
284
+ return () => this.listeners.delete(fn);
285
+ }
286
+ /** 取最后一次结果 */
287
+ getLastResult() {
288
+ return this.lastResult;
289
+ }
290
+ renderHeatmap(result) {
291
+ this.clear();
292
+ const mlMap = this.map.instance;
293
+ const data = heatmapToGeoJSON(result.heatmap);
294
+ const sourceId = `${this.layerPrefix}-heat-src`;
295
+ const layerId = `${this.layerPrefix}-heat-point`;
296
+ if (!mlMap.getSource(sourceId)) mlMap.addSource(sourceId, data);
297
+ try {
298
+ mlMap.addLayer({
299
+ id: layerId,
300
+ type: "heatmap",
301
+ source: sourceId,
302
+ paint: {
303
+ "heatmap-weight": [
304
+ "interpolate",
305
+ ["linear"],
306
+ ["get", "healthScore"],
307
+ 0,
308
+ 1,
309
+ 50,
310
+ 0.5,
311
+ 100,
312
+ 0
313
+ ],
314
+ "heatmap-intensity": 1,
315
+ "heatmap-color": [
316
+ "interpolate",
317
+ ["linear"],
318
+ ["heatmap-density"],
319
+ 0,
320
+ "rgba(34,197,94,0)",
321
+ 0.2,
322
+ "rgba(34,197,94,0.5)",
323
+ 0.5,
324
+ "rgba(245,158,11,0.7)",
325
+ 0.8,
326
+ "rgba(239,68,68,0.8)",
327
+ 1,
328
+ "rgba(127,29,29,0.9)"
329
+ ],
330
+ "heatmap-radius": 25,
331
+ "heatmap-opacity": 0.7
332
+ }
333
+ });
334
+ this.layerIds.push(layerId);
335
+ } catch {
336
+ }
337
+ }
338
+ };
339
+
340
+ exports.DEFAULT_WEIGHTS = DEFAULT_WEIGHTS;
341
+ exports.PipelineHealth = PipelineHealth;
342
+ exports.aggregateHeatmap = aggregateHeatmap;
343
+ exports.heatmapToGeoJSON = heatmapToGeoJSON;
344
+ exports.prioritizeMaintenance = prioritizeMaintenance;
345
+ exports.scorePipeHealth = scorePipeHealth;
346
+ exports.scorePipes = scorePipes;
347
+ //# sourceMappingURL=index.cjs.map
348
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/health/healthScorer.ts","../../src/health/riskHeatmap.ts","../../src/health/PipelineHealth.ts"],"names":[],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * 管线健康度评分(healthScorer)\n *\n * 6 维加权评分(PRD §4.4.2):\n * health_score = age (0.25) + material (0.20) + soil (0.15) +\n * history (0.20) + pressure (0.10) + protection (0.10)\n *\n * 每维 0-100,最终加权求和。\n * 评分模型可解释:每个维度的评分依据清晰可见。\n */\n\nimport type { PipeMaterial, PipeStatus } from '../types';\n\nexport interface PipeHealthInput {\n /** 安装日期 ISO */\n installDate?: string;\n /** 材质 */\n material?: PipeMaterial;\n /** 当前故障次数 */\n failureCount?: number;\n /** 当前压力(MPa) */\n pressure?: number;\n /** 最高允许压力(MPa) */\n ratedPressure?: number;\n /** 是否阴极保护 */\n hasCathodicProtection?: boolean;\n /** 土壤腐蚀指数 0-1(无数据时 0.5) */\n soilCorrosion?: number;\n /** 状态(damaged / under_repair 直接影响评分) */\n status?: PipeStatus;\n}\n\nexport interface PipeHealthScore {\n /** 总分 0-100 */\n score: number;\n /** 等级 */\n level: 'excellent' | 'good' | 'fair' | 'poor' | 'critical';\n /** 6 维细分 */\n dimensions: {\n age: { score: number; weight: number; reason: string };\n material: { score: number; weight: number; reason: string };\n soil: { score: number; weight: number; reason: string };\n history: { score: number; weight: number; reason: string };\n pressure: { score: number; weight: number; reason: string };\n protection: { score: number; weight: number; reason: string };\n };\n}\n\n/** 默认权重 */\nexport const DEFAULT_WEIGHTS = {\n age: 0.25,\n material: 0.2,\n soil: 0.15,\n history: 0.2,\n pressure: 0.1,\n protection: 0.1,\n};\n\nconst MATERIAL_RISK: Record<PipeMaterial, number> = {\n cast_iron: 0.85, // 铸铁 = 高风险(旧管网)\n ductile_iron: 0.2, // 球墨铸铁\n steel: 0.4,\n pe: 0.1,\n pvc: 0.15,\n concrete: 0.3,\n hdpe: 0.05,\n copper: 0.1,\n unknown: 0.5,\n};\n\n/**\n * 计算单根管线的健康度\n */\nexport function scorePipeHealth(input: PipeHealthInput, weights = DEFAULT_WEIGHTS): PipeHealthScore {\n const dims = {\n age: scoreAge(input, weights.age),\n material: scoreMaterial(input, weights.material),\n soil: scoreSoil(input, weights.soil),\n history: scoreHistory(input, weights.history),\n pressure: scorePressure(input, weights.pressure),\n protection: scoreProtection(input, weights.protection),\n };\n\n const total =\n dims.age.score * dims.age.weight +\n dims.material.score * dims.material.weight +\n dims.soil.score * dims.soil.weight +\n dims.history.score * dims.history.weight +\n dims.pressure.score * dims.pressure.weight +\n dims.protection.score * dims.protection.weight;\n\n // 损坏或维修中额外惩罚\n let final = total;\n if (input.status === 'damaged') final *= 0.5;\n else if (input.status === 'under_repair') final *= 0.7;\n else if (input.status === 'abandoned') final = 0;\n\n return {\n score: Math.round(final),\n level: toLevel(final),\n dimensions: dims,\n };\n}\n\nfunction toLevel(score: number): PipeHealthScore['level'] {\n if (score >= 80) return 'excellent';\n if (score >= 60) return 'good';\n if (score >= 40) return 'fair';\n if (score >= 20) return 'poor';\n return 'critical';\n}\n\nfunction scoreAge(input: PipeHealthInput, weight: number): PipeHealthScore['dimensions']['age'] {\n if (!input.installDate) {\n return { score: 50, weight, reason: '未知安装日期,按中等计' };\n }\n const ageYears = (Date.now() - new Date(input.installDate).getTime()) / (365.25 * 24 * 3600 * 1000);\n if (ageYears < 0) return { score: 100, weight, reason: '未来日期,视为优秀' };\n if (ageYears < 5) return { score: 100, weight, reason: '新建管线 (<5年)' };\n if (ageYears < 15) return { score: 85, weight, reason: '较新 (5-15年)' };\n if (ageYears < 25) return { score: 65, weight, reason: '中龄 (15-25年)' };\n if (ageYears < 40) return { score: 40, weight, reason: '老龄 (25-40年)' };\n if (ageYears < 60) return { score: 20, weight, reason: '超期 (>40年)' };\n return { score: 5, weight, reason: '超期 (>60年)' };\n}\n\nfunction scoreMaterial(input: PipeHealthInput, weight: number): PipeHealthScore['dimensions']['material'] {\n if (!input.material) {\n return { score: 50, weight, reason: '未知材质,按中等计' };\n }\n const risk = MATERIAL_RISK[input.material] ?? 0.5;\n const score = Math.round((1 - risk) * 100);\n const labels: Record<string, string> = {\n cast_iron: '铸铁(高风险)',\n ductile_iron: '球墨铸铁',\n steel: '钢',\n pe: 'PE(低风险)',\n pvc: 'PVC',\n concrete: '混凝土',\n hdpe: 'HDPE(最佳)',\n copper: '铜',\n unknown: '未知',\n };\n return {\n score,\n weight,\n reason: labels[input.material] ?? input.material,\n };\n}\n\nfunction scoreSoil(input: PipeHealthInput, weight: number): PipeHealthScore['dimensions']['soil'] {\n const c = input.soilCorrosion ?? 0.5;\n const score = Math.round((1 - c) * 100);\n return {\n score,\n weight,\n reason: c === 0.5 && !input.soilCorrosion ? '无土壤数据,按平均计' : `土壤腐蚀指数 ${c.toFixed(2)}`,\n };\n}\n\nfunction scoreHistory(input: PipeHealthInput, weight: number): PipeHealthScore['dimensions']['history'] {\n const fail = input.failureCount ?? 0;\n if (fail === 0) return { score: 100, weight, reason: '历史无故障' };\n if (fail === 1) return { score: 75, weight, reason: '历史 1 次故障' };\n if (fail === 2) return { score: 50, weight, reason: '历史 2 次故障' };\n if (fail === 3) return { score: 25, weight, reason: '历史 3 次故障' };\n return { score: 0, weight, reason: `历史 ≥${fail} 次故障` };\n}\n\nfunction scorePressure(input: PipeHealthInput, weight: number): PipeHealthScore['dimensions']['pressure'] {\n if (!input.pressure) {\n return { score: 75, weight, reason: '无压力数据,按中等偏好计' };\n }\n if (!input.ratedPressure) {\n return { score: 75, weight, reason: '仅知当前压力' };\n }\n const ratio = input.pressure / input.ratedPressure;\n if (ratio < 0.5) return { score: 60, weight, reason: '压力偏低 (使用率 <50%)' };\n if (ratio < 0.8) return { score: 90, weight, reason: '压力正常 (使用率 50-80%)' };\n if (ratio < 1.0) return { score: 75, weight, reason: '压力偏高 (使用率 80-100%)' };\n if (ratio < 1.2) return { score: 30, weight, reason: '压力超限 (100-120%)' };\n return { score: 0, weight, reason: '压力严重超限 (>120%)' };\n}\n\nfunction scoreProtection(input: PipeHealthInput, weight: number): PipeHealthScore['dimensions']['protection'] {\n if (input.hasCathodicProtection === true) {\n return { score: 90, weight, reason: '有阴极保护' };\n }\n if (input.hasCathodicProtection === false) {\n return { score: 30, weight, reason: '无阴极保护' };\n }\n return { score: 50, weight, reason: '阴保状态未知' };\n}\n\n/**\n * 批量评分(按需遍历)\n */\nexport function scorePipes(inputs: PipeHealthInput[], weights = DEFAULT_WEIGHTS): PipeHealthScore[] {\n return inputs.map((i) => scorePipeHealth(i, weights));\n}\n","/**\n * 风险热力图(riskHeatmap)\n *\n * 把 scorePipeHealth 输出按地理网格聚合,输出每个网格的平均分/最高风险,\n * 用于地图热力图(heat-map layer)。\n *\n * 输入:管线数组 [{ lng, lat, healthScore }]\n * 输出:GeoJSON FeatureCollection<Point>(含 healthScore 属性)\n */\n\nexport interface HeatmapCell {\n lng: number;\n lat: number;\n healthScore: number;\n /** 网格内管段数 */\n pipeCount: number;\n}\n\nexport interface PipeHealthPoint {\n lng: number;\n lat: number;\n healthScore: number;\n}\n\nexport interface RiskHeatmapOptions {\n /** 网格尺寸(m),默认 500 */\n cellSize?: number;\n /** 聚合方式 */\n aggregation?: 'average' | 'min' | 'max';\n}\n\nexport function aggregateHeatmap(\n points: PipeHealthPoint[],\n options: RiskHeatmapOptions = {}\n): HeatmapCell[] {\n const cell = options.cellSize ?? 500;\n const agg = options.aggregation ?? 'average';\n\n // 经纬度 → 米近似(以第一个点为原点)\n if (points.length === 0) return [];\n\n const refLat = points[0].lat;\n const refLng = points[0].lng;\n const mPerDegLat = 110_540;\n const mPerDegLng = 111_320 * Math.cos((refLat * Math.PI) / 180);\n\n const grid = new Map<string, { acc: number; count: number; minS: number; maxS: number; lng: number; lat: number }>();\n for (const p of points) {\n const x = (p.lng - refLng) * mPerDegLng;\n const y = (p.lat - refLat) * mPerDegLat;\n const cx = Math.floor(x / cell);\n const cy = Math.floor(y / cell);\n const key = `${cx}:${cy}`;\n const cur = grid.get(key);\n if (!cur) {\n grid.set(key, {\n acc: p.healthScore,\n count: 1,\n minS: p.healthScore,\n maxS: p.healthScore,\n lng: p.lng,\n lat: p.lat,\n });\n } else {\n cur.acc += p.healthScore;\n cur.count += 1;\n if (p.healthScore < cur.minS) cur.minS = p.healthScore;\n if (p.healthScore > cur.maxS) cur.maxS = p.healthScore;\n cur.lng = (cur.lng * (cur.count - 1) + p.lng) / cur.count;\n cur.lat = (cur.lat * (cur.count - 1) + p.lat) / cur.count;\n }\n }\n\n const cells: HeatmapCell[] = [];\n for (const v of grid.values()) {\n let score: number;\n if (agg === 'min') score = v.minS;\n else if (agg === 'max') score = v.maxS;\n else score = v.acc / v.count;\n cells.push({\n lng: v.lng,\n lat: v.lat,\n healthScore: Math.round(score),\n pipeCount: v.count,\n });\n }\n return cells;\n}\n\nexport function heatmapToGeoJSON(cells: HeatmapCell[]): GeoJSON.FeatureCollection<GeoJSON.Point> {\n return {\n type: 'FeatureCollection',\n features: cells.map((c) => ({\n type: 'Feature',\n geometry: { type: 'Point', coordinates: [c.lng, c.lat] },\n properties: {\n healthScore: c.healthScore,\n pipeCount: c.pipeCount,\n },\n })),\n };\n}\n\n/** 优先维护建议列表(风险最高的 Top N 管线) */\nexport function prioritizeMaintenance(\n pipes: Array<{ id: string; healthScore: number; lng: number; lat: number; label?: string }>,\n topN = 10\n) {\n return [...pipes].sort((a, b) => a.healthScore - b.healthScore).slice(0, topN);\n}\n","/**\n * PipelineHealth 组件类(地图集成层)\n */\n\nimport type { Map as CaoguoMap } from '@caoguo/maplibre';\nimport { scorePipeHealth, type PipeHealthInput, type PipeHealthScore, DEFAULT_WEIGHTS } from './healthScorer';\nimport { aggregateHeatmap, heatmapToGeoJSON, type HeatmapCell, prioritizeMaintenance } from './riskHeatmap';\nimport type { PipelineTopologyDataset } from '../types';\n\nexport interface PipelineHealthOptions {\n map: CaoguoMap;\n dataset: PipelineTopologyDataset;\n cellSize?: number;\n layerPrefix?: string;\n}\n\nexport interface HealthResult {\n /** 全部管段评分 */\n scores: Array<{ pipeId: string; score: PipeHealthScore }>;\n /** 热力图聚合格 */\n heatmap: HeatmapCell[];\n /** 优先维护建议 */\n maintenance: Array<{ id: string; healthScore: number; lng: number; lat: number; label?: string }>;\n /** 计算耗时 */\n durationMs: number;\n}\n\ntype Listener = (r: HealthResult) => void;\n\n/**\n * 管线健康评估组件\n */\nexport class PipelineHealth {\n private map: CaoguoMap;\n private dataset: PipelineTopologyDataset;\n private cellSize: number;\n private layerPrefix: string;\n private listeners = new Set<Listener>();\n private lastResult: HealthResult | null = null;\n private layerIds: string[] = [];\n\n constructor(options: PipelineHealthOptions) {\n this.map = options.map;\n this.dataset = options.dataset;\n this.cellSize = options.cellSize ?? 500;\n this.layerPrefix = options.layerPrefix ?? 'cg-health';\n }\n\n /** 评估全网管线健康度 */\n evaluate(weights = DEFAULT_WEIGHTS): HealthResult {\n const t0 = (typeof performance !== 'undefined' ? performance.now() : Date.now());\n const nodeById = new Map(this.dataset.nodes.map((n) => [n.id, n] as const));\n\n const scores = this.dataset.pipes.map((p) => {\n const input: PipeHealthInput = {\n installDate: p.properties?.installDate,\n material: p.properties?.material,\n failureCount: undefined,\n pressure: p.properties?.pressure,\n ratedPressure: p.properties?.ratedPressure,\n hasCathodicProtection: undefined,\n status: p.properties?.status,\n };\n return { pipeId: p.id, score: scorePipeHealth(input, weights) };\n });\n\n const points = scores.flatMap((s, i) => {\n const pipe = this.dataset.pipes[i];\n const from = nodeById.get(pipe.fromNode);\n const to = nodeById.get(pipe.toNode);\n if (!from || !to) return [];\n return [\n {\n lng: (from.lng + to.lng) / 2,\n lat: (from.lat + to.lat) / 2,\n healthScore: s.score.score,\n },\n ];\n });\n\n const heatmap = aggregateHeatmap(points, { cellSize: this.cellSize });\n const maintenance = prioritizeMaintenance(\n scores.flatMap((s, i) => {\n const pipe = this.dataset.pipes[i];\n const from = nodeById.get(pipe.fromNode);\n const to = nodeById.get(pipe.toNode);\n if (!from || !to) return [];\n return [\n {\n id: pipe.id,\n healthScore: s.score.score,\n lng: (from.lng + to.lng) / 2,\n lat: (from.lat + to.lat) / 2,\n label: pipe.id,\n },\n ];\n })\n );\n\n const durationMs =\n (typeof performance !== 'undefined' ? performance.now() : Date.now()) - t0;\n\n const result: HealthResult = { scores, heatmap, maintenance, durationMs };\n this.lastResult = result;\n this.renderHeatmap(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(): HealthResult | null {\n return this.lastResult;\n }\n\n private renderHeatmap(result: HealthResult): void {\n this.clear();\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 const data = heatmapToGeoJSON(result.heatmap);\n const sourceId = `${this.layerPrefix}-heat-src`;\n const layerId = `${this.layerPrefix}-heat-point`;\n if (!mlMap.getSource(sourceId)) mlMap.addSource(sourceId, data);\n try {\n mlMap.addLayer({\n id: layerId,\n type: 'heatmap',\n source: sourceId,\n paint: {\n 'heatmap-weight': [\n 'interpolate',\n ['linear'],\n ['get', 'healthScore'],\n 0, 1,\n 50, 0.5,\n 100, 0,\n ] as unknown,\n 'heatmap-intensity': 1,\n 'heatmap-color': [\n 'interpolate',\n ['linear'],\n ['heatmap-density'],\n 0, 'rgba(34,197,94,0)',\n 0.2, 'rgba(34,197,94,0.5)',\n 0.5, 'rgba(245,158,11,0.7)',\n 0.8, 'rgba(239,68,68,0.8)',\n 1, 'rgba(127,29,29,0.9)',\n ] as unknown,\n 'heatmap-radius': 25,\n 'heatmap-opacity': 0.7,\n },\n });\n this.layerIds.push(layerId);\n } catch {\n // ignore\n }\n }\n}\n"]}
@@ -0,0 +1,3 @@
1
+ export { DEFAULT_WEIGHTS, PipelineHealth, aggregateHeatmap, heatmapToGeoJSON, prioritizeMaintenance, scorePipeHealth, scorePipes } from '../chunk-VA2TQWL3.js';
2
+ //# sourceMappingURL=index.js.map
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"index.js"}