@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
@@ -1,5 +1,7 @@
1
1
  'use strict';
2
2
 
3
+ var maplibre = require('@caoguo/maplibre');
4
+
3
5
  // src/leakage/pasquillGifford.ts
4
6
  var TABLE_DAY = [
5
7
  { wind: 2, cls: { strong: "A", moderate: "A", slight: "B" } },
@@ -193,8 +195,6 @@ function convexHullLocal(points) {
193
195
  lower.pop();
194
196
  return lower.concat(upper);
195
197
  }
196
-
197
- // src/leakage/LeakagePlume.ts
198
198
  var LeakagePlume = class {
199
199
  map;
200
200
  thresholds;
@@ -268,7 +268,7 @@ var LeakagePlume = class {
268
268
  properties: { threshold: 0 }
269
269
  };
270
270
  const id = `${this.layerPrefix}-flood-fill`;
271
- if (!mlMap.getSource(id)) mlMap.addSource(id, data);
271
+ maplibre.upsertSource(mlMap, id, data);
272
272
  try {
273
273
  mlMap.addLayer({
274
274
  id,
@@ -296,7 +296,7 @@ var LeakagePlume = class {
296
296
  };
297
297
  const sourceId = `${this.layerPrefix}-gas-src`;
298
298
  const layerId = `${this.layerPrefix}-gas-fill`;
299
- if (!mlMap.getSource(sourceId)) mlMap.addSource(sourceId, data);
299
+ maplibre.upsertSource(mlMap, sourceId, data);
300
300
  try {
301
301
  mlMap.addLayer({
302
302
  id: layerId,
@@ -320,7 +320,7 @@ var LeakagePlume = class {
320
320
  } catch {
321
321
  }
322
322
  const ptId = `${this.layerPrefix}-source`;
323
- mlMap.addSource(ptId, {
323
+ const ptData = {
324
324
  type: "FeatureCollection",
325
325
  features: [
326
326
  {
@@ -329,7 +329,8 @@ var LeakagePlume = class {
329
329
  properties: {}
330
330
  }
331
331
  ]
332
- });
332
+ };
333
+ maplibre.upsertSource(mlMap, ptId, ptData);
333
334
  try {
334
335
  mlMap.addLayer({
335
336
  id: ptId,
@@ -1 +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":"index.cjs","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"]}
1
+ {"version":3,"sources":["../../src/leakage/pasquillGifford.ts","../../src/leakage/gaussianPlume.ts","../../src/leakage/floodFill.ts","../../src/leakage/LeakagePlume.ts"],"names":["upsertSource"],"mappings":"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","file":"index.cjs","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 { upsertSource } 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 upsertSource(mlMap, 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 upsertSource(mlMap, 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 // 标记泄漏点(幂等:模拟重渲染时 source 可能已存在)\n const ptId = `${this.layerPrefix}-source`;\n const ptData = {\n type: 'FeatureCollection' as const,\n features: [\n {\n type: 'Feature' as const,\n geometry: { type: 'Point' as const, coordinates: [source.lng, source.lat] },\n properties: {},\n },\n ],\n };\n upsertSource(mlMap, ptId, ptData);\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"]}
@@ -1,3 +1,3 @@
1
- export { LeakagePlume, classifyFromMeteo, classifyStability, dispersionCoefficients, gaussianPlume, simulateFlood } from '../chunk-GEJRE2OR.js';
1
+ export { LeakagePlume, classifyFromMeteo, classifyStability, dispersionCoefficients, gaussianPlume, simulateFlood } from '../chunk-PJMHTPSA.js';
2
2
  //# sourceMappingURL=index.js.map
3
3
  //# sourceMappingURL=index.js.map
@@ -1,5 +1,9 @@
1
1
  'use strict';
2
2
 
3
+ var maplibre = require('@caoguo/maplibre');
4
+
5
+ // src/topology/PipelineTopology.ts
6
+
3
7
  // src/style/pipelineTheme.ts
4
8
  var PIPELINE_TYPE_COLORS = {
5
9
  gas: "#f59e0b",
@@ -164,6 +168,12 @@ var PipelineTopology = class {
164
168
  nodeListeners = /* @__PURE__ */ new Set();
165
169
  pipeListeners = /* @__PURE__ */ new Set();
166
170
  drillListeners = /* @__PURE__ */ new Set();
171
+ /** 当前钻取区域(null = 全量) */
172
+ currentRegion = null;
173
+ /** 当前分层过滤器 */
174
+ layerFilter = null;
175
+ /** click 事件是否已绑定 */
176
+ clickBound = false;
167
177
  constructor(options) {
168
178
  this.map = options.map;
169
179
  this.dataset = options.dataset;
@@ -178,12 +188,7 @@ var PipelineTopology = class {
178
188
  const prefix = this.layerPrefix;
179
189
  const pipeGeoJSON = {
180
190
  type: "FeatureCollection",
181
- features: this.dataset.pipes.filter((p) => {
182
- if (this.pipelineTypes?.length && !this.pipelineTypes.includes(p.pipelineType)) {
183
- return false;
184
- }
185
- return true;
186
- }).flatMap((p) => {
191
+ features: this.dataset.pipes.filter((p) => this.isPipeVisible(p)).flatMap((p) => {
187
192
  const from = this.dataset.nodes.find((n) => n.id === p.fromNode);
188
193
  const to = this.dataset.nodes.find((n) => n.id === p.toNode);
189
194
  if (!from || !to) return [];
@@ -208,19 +213,14 @@ var PipelineTopology = class {
208
213
  };
209
214
  const nodeGeoJSON = {
210
215
  type: "FeatureCollection",
211
- features: this.dataset.nodes.filter((n) => {
212
- if (this.pipelineTypes?.length && !this.pipelineTypes.includes(n.pipelineType)) {
213
- return false;
214
- }
215
- return true;
216
- }).map((n) => ({
216
+ features: this.dataset.nodes.filter((n) => this.isNodeVisible(n)).map((n) => ({
217
217
  type: "Feature",
218
218
  geometry: { type: "Point", coordinates: [n.lng, n.lat] },
219
219
  properties: { nodeId: n.id, kind: n.kind, pipelineType: n.pipelineType ?? "" }
220
220
  }))
221
221
  };
222
- mlMap.addSource(`${prefix}-pipes-src`, pipeGeoJSON);
223
- mlMap.addSource(`${prefix}-nodes-src`, nodeGeoJSON);
222
+ maplibre.upsertSource(mlMap, `${prefix}-pipes-src`, pipeGeoJSON);
223
+ maplibre.upsertSource(mlMap, `${prefix}-nodes-src`, nodeGeoJSON);
224
224
  mlMap.addLayer({
225
225
  id: `${prefix}-pipes-line`,
226
226
  type: "line",
@@ -243,6 +243,33 @@ var PipelineTopology = class {
243
243
  }
244
244
  });
245
245
  this.layerIds.push(`${prefix}-pipes-line`, `${prefix}-nodes-pt`);
246
+ this.bindClickEvents(mlMap, prefix);
247
+ }
248
+ /**
249
+ * 绑定点击事件(设备卡片:点击节点/管段触发订阅回调)。
250
+ * 只绑定一次(render 重建图层后事件仍指向固定 layerId)。
251
+ */
252
+ bindClickEvents(mlMap, prefix) {
253
+ if (this.clickBound) return;
254
+ this.clickBound = true;
255
+ const nodeById = new Map(this.dataset.nodes.map((n) => [n.id, n]));
256
+ const pipeById = new Map(this.dataset.pipes.map((p) => [p.id, p]));
257
+ mlMap.on("click", `${prefix}-pipes-line`, (e) => {
258
+ const features = e.features ?? [];
259
+ const f = features[0];
260
+ const pipe = f?.properties?.pipeId ? pipeById.get(f.properties.pipeId) : void 0;
261
+ if (pipe) {
262
+ for (const l of this.pipeListeners) l({ pipe });
263
+ }
264
+ });
265
+ mlMap.on("click", `${prefix}-nodes-pt`, (e) => {
266
+ const features = e.features ?? [];
267
+ const f = features[0];
268
+ const node = f?.properties?.nodeId ? nodeById.get(f.properties.nodeId) : void 0;
269
+ if (node) {
270
+ for (const l of this.nodeListeners) l({ node });
271
+ }
272
+ });
246
273
  }
247
274
  /** 切换着色模式 */
248
275
  setColorBy(mode) {
@@ -288,6 +315,78 @@ var PipelineTopology = class {
288
315
  this.drillListeners.add(fn);
289
316
  return () => this.drillListeners.delete(fn);
290
317
  }
318
+ /** 管段是否可见(管线类型 + 区域钻取 + 分层过滤) */
319
+ isPipeVisible(p) {
320
+ if (this.pipelineTypes?.length && !this.pipelineTypes.includes(p.pipelineType)) {
321
+ return false;
322
+ }
323
+ if (this.currentRegion && p.region !== this.currentRegion) return false;
324
+ const f = this.layerFilter;
325
+ if (f) {
326
+ const props = p.properties ?? {};
327
+ if (f.minDiameter !== void 0 && (props.diameter ?? 0) < f.minDiameter) return false;
328
+ if (f.maxDiameter !== void 0 && (props.diameter ?? Infinity) > f.maxDiameter) return false;
329
+ if (f.material && props.material !== f.material) return false;
330
+ if (f.status && props.status !== f.status) return false;
331
+ if (f.minAgeYears !== void 0 && props.installDate) {
332
+ if (this.ageInYears(props.installDate) < f.minAgeYears) return false;
333
+ }
334
+ }
335
+ return true;
336
+ }
337
+ /** 节点是否可见(管线类型 + 区域钻取) */
338
+ isNodeVisible(n) {
339
+ if (this.pipelineTypes?.length && !this.pipelineTypes.includes(n.pipelineType)) {
340
+ return false;
341
+ }
342
+ if (this.currentRegion && n.region !== this.currentRegion) return false;
343
+ return true;
344
+ }
345
+ /** 计算管段使用年限(年) */
346
+ ageInYears(installDate) {
347
+ const d = new Date(installDate).getTime();
348
+ if (Number.isNaN(d)) return 0;
349
+ return (Date.now() - d) / (365.25 * 24 * 3600 * 1e3);
350
+ }
351
+ /**
352
+ * 搜索定位(按编号/地址/区域/类型)。
353
+ * 返回匹配的节点与管段,供上层 flyTo 定位。
354
+ */
355
+ search(query) {
356
+ const q = query.trim().toLowerCase();
357
+ if (!q) return { nodes: [], pipes: [] };
358
+ const nodes = this.dataset.nodes.filter(
359
+ (n) => n.id.toLowerCase().includes(q) || (n.properties?.code ?? "").toLowerCase().includes(q) || (n.region ?? "").toLowerCase().includes(q) || n.kind.toLowerCase().includes(q)
360
+ );
361
+ const pipes = this.dataset.pipes.filter(
362
+ (p) => p.id.toLowerCase().includes(q) || (p.region ?? "").toLowerCase().includes(q) || (p.properties?.material ?? "").toLowerCase().includes(q)
363
+ );
364
+ return { nodes, pipes };
365
+ }
366
+ /** 层级钻取:下钻到指定区域(过滤渲染 + 触发事件) */
367
+ drillDown(region) {
368
+ const from = this.currentRegion;
369
+ this.currentRegion = region;
370
+ this.render();
371
+ for (const l of this.drillListeners) l({ from, to: region });
372
+ }
373
+ /** 层级钻取:返回上一级(清空区域过滤) */
374
+ drillUp() {
375
+ const from = this.currentRegion;
376
+ this.currentRegion = null;
377
+ this.render();
378
+ for (const l of this.drillListeners) l({ from, to: "" });
379
+ }
380
+ /** 分层控制:按管径/材质/状态/年代过滤 */
381
+ setLayerFilter(filter) {
382
+ this.layerFilter = filter;
383
+ this.render();
384
+ }
385
+ /** 清空分层过滤器 */
386
+ clearLayerFilter() {
387
+ this.layerFilter = null;
388
+ this.render();
389
+ }
291
390
  /** 高亮连通路径(基于节点 ID) */
292
391
  highlightConnectivity(centerId) {
293
392
  const adj = /* @__PURE__ */ new Map();
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/style/pipelineTheme.ts","../../src/style/paintRules.ts","../../src/topology/PipelineTopology.ts"],"names":[],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * 草果地图管网专用样式 - `caoguo-pipeline`\n *\n * 设计目标:\n * - 行业辨识度高:燃气=琥珀、供水=蓝、供热=红、电力=紫、通信=绿、排水=灰\n * - 状态语义强:正常/老化/损坏/维修中 各有专属色 + 动画\n * - 与 caoguo-dark/light 主题协调:在亮/暗模式下都可读\n *\n * 颜色规范依据 PRD §4.5。\n */\n\nimport type { PipelineType, PipeMaterial, PipeStatus, NodeKind } from '../types';\n\n/** 管线大类配色(caoguo-pipeline) */\nexport const PIPELINE_TYPE_COLORS: Record<PipelineType, string> = {\n gas: '#f59e0b', // 琥珀(燃气)\n water: '#3b82f6', // 蓝(供水)\n drainage: '#6b7280', // 灰(排水)\n heating: '#ef4444', // 红(供热)\n power: '#8b5cf6', // 紫(电力管沟)\n telecom: '#10b981', // 绿(通信管沟)\n};\n\n/** 管线大类的中文标签(地图图例/卡片显示) */\nexport const PIPELINE_TYPE_LABELS: Record<PipelineType, string> = {\n gas: '燃气管',\n water: '供水管',\n drainage: '排水管',\n heating: '供热管',\n power: '电力管沟',\n telecom: '通信管沟',\n};\n\n/** 管线大类的 Emoji 图标(轻量可视化标识,用于卡片/Tag) */\nexport const PIPELINE_TYPE_ICONS: Record<PipelineType, string> = {\n gas: '🔥',\n water: '💧',\n drainage: '🚰',\n heating: '🔴',\n power: '⚡',\n telecom: '📡',\n};\n\n/** 管段状态配色 */\nexport const PIPE_STATUS_COLORS: Record<PipeStatus, string> = {\n normal: '#4ade80', // 绿\n aging: '#fbbf24', // 黄\n damaged: '#ef4444', // 红\n under_repair: '#8b5cf6', // 紫\n abandoned: '#4b5563', // 深灰\n unknown: '#6b7280', // 灰\n};\n\n/** 状态的语义 + 动画 */\nexport const PIPE_STATUS_META: Record<\n PipeStatus,\n { label: string; animation?: 'pulse' | 'dashed' | 'none' }\n> = {\n normal: { label: '正常', animation: 'none' },\n aging: { label: '老化', animation: 'none' },\n damaged: { label: '损坏', animation: 'pulse' },\n under_repair: { label: '维修中', animation: 'dashed' },\n abandoned: { label: '废弃', animation: 'none' },\n unknown: { label: '未知', animation: 'none' },\n};\n\n/** 管材配色(maplibre style 用) */\nexport const PIPE_MATERIAL_COLORS: Record<PipeMaterial, string> = {\n cast_iron: '#94a3b8', // 蓝灰(旧铸铁)\n ductile_iron: '#0ea5e9', // 天蓝(球墨铸铁)\n steel: '#64748b', // 钢蓝\n pe: '#22c55e', // 绿(PE 现代管材)\n pvc: '#a78bfa', // 紫(PVC)\n concrete: '#a8a29e', // 米灰(混凝土)\n hdpe: '#16a34a', // 深绿(HDPE)\n copper: '#d97706', // 橙(铜)\n unknown: '#6b7280',\n};\n\n/** 节点图标(用 SVG path 描点,地图渲染层用) */\nexport const NODE_KIND_ICONS: Record<NodeKind, string> = {\n junction: 'M -6 0 L 6 0 M 0 -6 L 0 6', // 十字\n valve: 'M -8 -4 L 8 4 M 0 -4 L 0 4', // X\n pump: 'M -6 0 A 6 6 0 1 0 6 0 A 6 6 0 1 0 -6 0', // 圆\n meter: 'M -6 -6 L 6 -6 L 6 6 L -6 6 Z', // 方\n source: 'M 0 -10 L 6 6 L -6 6 Z', // 三角\n tank: 'M -8 -6 Q 0 -10 8 -6 L 8 6 L -8 6 Z', // 圆角\n junction_box: 'M -7 -4 L 7 -4 L 7 4 L -7 4 Z',// 小方\n};\n\n/** 节点配色(默认按 type 衍生) */\nexport const NODE_KIND_COLORS: Record<NodeKind, string> = {\n junction: '#94a3b8',\n valve: '#ef4444', // 阀门=红(控制关键)\n pump: '#f59e0b',\n meter: '#3b82f6',\n source: '#22c55e',\n tank: '#06b6d4',\n junction_box: '#8b5cf6',\n};\n\n/** 健康度等级配色(PipelineHealth 用) */\nexport const HEALTH_LEVEL_COLORS: Record<string, string> = {\n excellent: '#22c55e', // 绿 80-100\n good: '#3b82f6', // 蓝 60-80\n fair: '#eab308', // 黄 40-60\n poor: '#f97316', // 橙 20-40\n critical: '#ef4444', // 红 0-20\n};\n\n/** 健康等级 label */\nexport const HEALTH_LEVEL_LABELS: Record<string, string> = {\n excellent: '优',\n good: '良',\n fair: '中',\n poor: '差',\n critical: '危',\n};\n\n/**\n * 健康度 → 等级(保留闭区间,遵守 PRD §4.4.2)\n * @param score 0-100 健康分\n */\nexport function healthLevel(score: number): 'excellent' | 'good' | 'fair' | 'poor' | 'critical' {\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\n/** 按管线类型取默认色(兜底用) */\nexport function pipelineTypeColor(type: PipelineType | undefined): string {\n if (!type) return '#94a3b8';\n return PIPELINE_TYPE_COLORS[type];\n}\n","/**\n * 管网 MapLibre paint 规则(caoguo-pipeline)\n *\n * 把\"管线/节点如何着色\"的业务规则翻译成 MapLibre style spec,\n * 业务方可直接 addLayer / setPaintProperty 使用。\n *\n * 规则集:\n * - 管线按管线大类着色(type)\n * - 管线按管径着色(diameter,PRD §4.1.4)\n * - 管线按状态着色(status)\n * - 管线按材质着色(material)\n * - 管线按健康度着色(health,PipelineHealth 集成)\n *\n * 节点按节点类型着色 + 图标。\n */\n\nimport type {\n PipelineType,\n PipeMaterial,\n PipeStatus,\n ColorByMode,\n} from '../types';\nimport { PIPELINE_TYPE_COLORS, PIPE_STATUS_COLORS, HEALTH_LEVEL_COLORS, PIPE_MATERIAL_COLORS } from './pipelineTheme';\n\n/**\n * MapLibre style spec 的 color function DSL(typed),\n * 业务方可直接 `map.addLayer({type:'line', paint: {'line-color': rule} })`\n *\n * MapLibre 表达式其实接受 string | number | 嵌套数组,所以我们用 unknown 让 TS 放行\n */\nexport type PaintRule = unknown;\n\n/** 管线按\"管线大类\"着色 */\nexport function paintPipeByType(types?: PipelineType[]): PaintRule {\n if (!types || types.length === 0) {\n // 全部类型都涂色\n return [\n 'match',\n ['get', 'pipelineType'],\n ...Object.entries(PIPELINE_TYPE_COLORS).flatMap(([k, v]) => [k, v]),\n '#94a3b8',\n ];\n }\n return [\n 'match',\n ['get', 'pipelineType'],\n ...types.flatMap((t) => [t, PIPELINE_TYPE_COLORS[t]]),\n '#94a3b8',\n ];\n}\n\n/** 管线按\"管径\"渐变着色(PRD §4.1.4) */\nexport function paintPipeByDiameter(): PaintRule {\n return [\n 'interpolate',\n ['linear'],\n ['coalesce', ['get', 'diameter'], 0],\n 0, '#4ade80', // 极细管 绿\n 50, '#4ade80', // DN50 以下 绿(支管)\n 150, '#60a5fa', // DN150 蓝(配水管)\n 300, '#f59e0b', // DN300 黄(干管)\n 600, '#ef4444', // DN600+ 红(主管)\n 1500, '#7f1d1d', // 极大管 深红\n ];\n}\n\n/** 管线按\"状态\"着色 */\nexport function paintPipeByStatus(): PaintRule {\n return [\n 'match',\n ['coalesce', ['get', 'status'], 'unknown'],\n ...Object.entries(PIPE_STATUS_COLORS).flatMap(([k, v]) => [k, v]),\n '#6b7280',\n ];\n}\n\n/** 管线按\"材质\"着色 */\nexport function paintPipeByMaterial(): PaintRule {\n return [\n 'match',\n ['coalesce', ['get', 'material'], 'unknown'],\n ...Object.entries(PIPE_MATERIAL_COLORS).flatMap(([k, v]) => [k, v]),\n '#6b7280',\n ];\n}\n\n/** 管线按\"健康度\"着色(健康分 0-100,规则按 healthLevel 离散) */\nexport function paintPipeByHealth(): PaintRule {\n return [\n 'step',\n ['coalesce', ['get', 'healthScore'], 100],\n HEALTH_LEVEL_COLORS.critical, // <20\n 20, HEALTH_LEVEL_COLORS.poor,\n 40, HEALTH_LEVEL_COLORS.fair,\n 60, HEALTH_LEVEL_COLORS.good,\n 80, HEALTH_LEVEL_COLORS.excellent,\n ];\n}\n\n/** 根据模式返回 paint rule 工厂 */\nexport function paintPipeBy(mode: ColorByMode, opts?: { types?: PipelineType[] }): PaintRule {\n switch (mode) {\n case 'type':\n return paintPipeByType(opts?.types);\n case 'diameter':\n return paintPipeByDiameter();\n case 'status':\n return paintPipeByStatus();\n case 'material':\n return paintPipeByMaterial();\n case 'health':\n return paintPipeByHealth();\n case 'uniform':\n default:\n return '#60a5fa';\n }\n}\n\n/** 节点按 kind 着色(maplibre style circle-color 用) */\nexport function paintNodeByKind(): PaintRule {\n return [\n 'match',\n ['get', 'kind'],\n 'junction', '#94a3b8',\n 'valve', '#ef4444',\n 'pump', '#f59e0b',\n 'meter', '#3b82f6',\n 'source', '#22c55e',\n 'tank', '#06b6d4',\n 'junction_box', '#8b5cf6',\n '#cbd5e1',\n ];\n}\n\n/** 默认管段宽度(按管径递推) */\nexport function paintPipeWidthByDiameter(minWidth = 1.5, maxWidth = 6): PaintRule {\n return [\n 'interpolate',\n ['linear'],\n ['coalesce', ['get', 'diameter'], 100],\n 0, minWidth,\n 1500, maxWidth,\n ];\n}\n","/**\n * PipelineTopology 类 - 占位(待 M4 W3-W4 实现)\n *\n * 本文件作为 Phase 1 管线拓扑编辑器的入口 stub,后续完整实现包含:\n * - 拓扑渲染(管段/节点按类型着色)\n * - 层级钻取(区域→街道→小区→楼栋)\n * - 设备卡片(点击节点/管段弹出属性面板)\n * - 连通性高亮(选中管线后高亮上下游)\n * - 搜索定位(按编号/地址/区域)\n * - 分层控制(按管径/材质/年代/状态)\n */\n\nimport type { Map as CaoguoMap } from '@caoguo/maplibre';\nimport type {\n PipelineTopologyDataset,\n ColorByMode,\n PipelineType,\n NodeSelectEvent,\n PipeSelectEvent,\n DrillDownEvent,\n} from '../types';\nimport { paintPipeBy } from '../style/paintRules';\n\nexport interface PipelineTopologyOptions {\n map: CaoguoMap;\n dataset: PipelineTopologyDataset;\n /** 默认着色模式 */\n colorBy?: ColorByMode;\n /** 仅显示指定管线类型(空 = 全显示) */\n pipelineTypes?: PipelineType[];\n /** 层 ID 前缀 */\n layerPrefix?: string;\n}\n\ntype NodeListener = (e: NodeSelectEvent) => void;\ntype PipeListener = (e: PipeSelectEvent) => void;\ntype DrillListener = (e: DrillDownEvent) => void;\n\n/**\n * PipelineTopology 组件(占位 stub)\n *\n * Phase 1 MVP 实现进度:\n * - [x] 渲染数据接入\n * - [ ] 层级钻取(M4 W3-W4 后续完善)\n * - [ ] 设备卡片(M4 W3-W4 后续完善)\n * - [ ] 连通性高亮(M5 W3-W4 后续完善)\n * - [ ] 搜索定位(M5 W3-W4 后续完善)\n * - [ ] 分层控制(M5 W3-W4 后续完善)\n */\nexport class PipelineTopology {\n private map: CaoguoMap;\n private dataset: PipelineTopologyDataset;\n private colorBy: ColorByMode;\n private layerPrefix: string;\n private pipelineTypes?: PipelineType[];\n private layerIds: string[] = [];\n private nodeListeners = new Set<NodeListener>();\n private pipeListeners = new Set<PipeListener>();\n private drillListeners = new Set<DrillListener>();\n\n constructor(options: PipelineTopologyOptions) {\n this.map = options.map;\n this.dataset = options.dataset;\n this.colorBy = options.colorBy ?? 'type';\n this.pipelineTypes = options.pipelineTypes;\n this.layerPrefix = options.layerPrefix ?? 'cg-topo';\n }\n\n /** 渲染管段+节点到地图 */\n render(): 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 prefix = this.layerPrefix;\n\n const pipeGeoJSON: GeoJSON.FeatureCollection<GeoJSON.LineString> = {\n type: 'FeatureCollection',\n features: this.dataset.pipes\n .filter((p) => {\n if (this.pipelineTypes?.length && !this.pipelineTypes.includes(p.pipelineType as PipelineType)) {\n return false;\n }\n return true;\n })\n .flatMap((p) => {\n const from = this.dataset.nodes.find((n) => n.id === p.fromNode);\n const to = this.dataset.nodes.find((n) => n.id === p.toNode);\n if (!from || !to) return [];\n const coords: [number, number][] =\n p.geometry && p.geometry.length >= 2\n ? (p.geometry as [number, number][])\n : [\n [from.lng, from.lat],\n [to.lng, to.lat],\n ];\n return [\n {\n type: 'Feature' as const,\n geometry: { type: 'LineString' as const, coordinates: coords },\n properties: {\n pipeId: p.id,\n pipelineType: p.pipelineType ?? '',\n diameter: p.properties?.diameter ?? 0,\n status: p.properties?.status ?? 'unknown',\n material: p.properties?.material ?? 'unknown',\n },\n },\n ];\n }),\n };\n\n const nodeGeoJSON: GeoJSON.FeatureCollection<GeoJSON.Point> = {\n type: 'FeatureCollection',\n features: this.dataset.nodes\n .filter((n) => {\n if (this.pipelineTypes?.length && !this.pipelineTypes.includes(n.pipelineType as PipelineType)) {\n return false;\n }\n return true;\n })\n .map((n) => ({\n type: 'Feature' as const,\n geometry: { type: 'Point' as const, coordinates: [n.lng, n.lat] },\n properties: { nodeId: n.id, kind: n.kind, pipelineType: n.pipelineType ?? '' },\n })),\n };\n\n mlMap.addSource(`${prefix}-pipes-src`, pipeGeoJSON);\n mlMap.addSource(`${prefix}-nodes-src`, nodeGeoJSON);\n mlMap.addLayer({\n id: `${prefix}-pipes-line`,\n type: 'line',\n source: `${prefix}-pipes-src`,\n paint: {\n 'line-color': paintPipeBy(this.colorBy, { types: this.pipelineTypes }) as never,\n 'line-width': 2,\n 'line-opacity': 0.85,\n },\n });\n mlMap.addLayer({\n id: `${prefix}-nodes-pt`,\n type: 'circle',\n source: `${prefix}-nodes-src`,\n paint: {\n 'circle-radius': 4,\n 'circle-color': '#f59e0b',\n 'circle-stroke-width': 1.5,\n 'circle-stroke-color': '#ffffff',\n },\n });\n this.layerIds.push(`${prefix}-pipes-line`, `${prefix}-nodes-pt`);\n }\n\n /** 切换着色模式 */\n setColorBy(mode: ColorByMode): void {\n this.colorBy = mode;\n const mlMap = (this.map as unknown as {\n instance: { setPaintProperty: (id: string, prop: string, value: unknown) => void };\n }).instance;\n if (mlMap.setPaintProperty) {\n try {\n mlMap.setPaintProperty(\n `${this.layerPrefix}-pipes-line`,\n 'line-color',\n paintPipeBy(mode, { types: this.pipelineTypes }) as never\n );\n } catch {\n // ignore\n }\n }\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.nodeListeners.clear();\n this.pipeListeners.clear();\n this.drillListeners.clear();\n }\n\n /** 订阅节点点击事件 */\n onNodeSelect(fn: NodeListener): () => void {\n this.nodeListeners.add(fn);\n return () => this.nodeListeners.delete(fn);\n }\n\n /** 订阅管段点击事件 */\n onPipeSelect(fn: PipeListener): () => void {\n this.pipeListeners.add(fn);\n return () => this.pipeListeners.delete(fn);\n }\n\n /** 订阅钻取事件 */\n onDrillDown(fn: DrillListener): () => void {\n this.drillListeners.add(fn);\n return () => this.drillListeners.delete(fn);\n }\n\n /** 高亮连通路径(基于节点 ID) */\n highlightConnectivity(centerId: string): Set<string> {\n const adj = new Map<string, Set<string>>();\n for (const p of this.dataset.pipes) {\n if (!adj.has(p.fromNode)) adj.set(p.fromNode, new Set());\n if (!adj.has(p.toNode)) adj.set(p.toNode, new Set());\n adj.get(p.fromNode)!.add(p.toNode);\n adj.get(p.toNode)!.add(p.fromNode);\n }\n\n const visited = new Set<string>();\n const stack = [centerId];\n while (stack.length > 0) {\n const cur = stack.pop()!;\n if (visited.has(cur)) continue;\n visited.add(cur);\n const neighbors = adj.get(cur) ?? new Set();\n for (const n of neighbors) {\n if (!visited.has(n)) stack.push(n);\n }\n }\n return visited;\n }\n}\n"]}
1
+ {"version":3,"sources":["../../src/style/pipelineTheme.ts","../../src/style/paintRules.ts","../../src/topology/PipelineTopology.ts"],"names":["upsertSource"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * 草果地图管网专用样式 - `caoguo-pipeline`\n *\n * 设计目标:\n * - 行业辨识度高:燃气=琥珀、供水=蓝、供热=红、电力=紫、通信=绿、排水=灰\n * - 状态语义强:正常/老化/损坏/维修中 各有专属色 + 动画\n * - 与 caoguo-dark/light 主题协调:在亮/暗模式下都可读\n *\n * 颜色规范依据 PRD §4.5。\n */\n\nimport type { PipelineType, PipeMaterial, PipeStatus, NodeKind } from '../types';\n\n/** 管线大类配色(caoguo-pipeline) */\nexport const PIPELINE_TYPE_COLORS: Record<PipelineType, string> = {\n gas: '#f59e0b', // 琥珀(燃气)\n water: '#3b82f6', // 蓝(供水)\n drainage: '#6b7280', // 灰(排水)\n heating: '#ef4444', // 红(供热)\n power: '#8b5cf6', // 紫(电力管沟)\n telecom: '#10b981', // 绿(通信管沟)\n};\n\n/** 管线大类的中文标签(地图图例/卡片显示) */\nexport const PIPELINE_TYPE_LABELS: Record<PipelineType, string> = {\n gas: '燃气管',\n water: '供水管',\n drainage: '排水管',\n heating: '供热管',\n power: '电力管沟',\n telecom: '通信管沟',\n};\n\n/** 管线大类的 Emoji 图标(轻量可视化标识,用于卡片/Tag) */\nexport const PIPELINE_TYPE_ICONS: Record<PipelineType, string> = {\n gas: '🔥',\n water: '💧',\n drainage: '🚰',\n heating: '🔴',\n power: '⚡',\n telecom: '📡',\n};\n\n/** 管段状态配色 */\nexport const PIPE_STATUS_COLORS: Record<PipeStatus, string> = {\n normal: '#4ade80', // 绿\n aging: '#fbbf24', // 黄\n damaged: '#ef4444', // 红\n under_repair: '#8b5cf6', // 紫\n abandoned: '#4b5563', // 深灰\n unknown: '#6b7280', // 灰\n};\n\n/** 状态的语义 + 动画 */\nexport const PIPE_STATUS_META: Record<\n PipeStatus,\n { label: string; animation?: 'pulse' | 'dashed' | 'none' }\n> = {\n normal: { label: '正常', animation: 'none' },\n aging: { label: '老化', animation: 'none' },\n damaged: { label: '损坏', animation: 'pulse' },\n under_repair: { label: '维修中', animation: 'dashed' },\n abandoned: { label: '废弃', animation: 'none' },\n unknown: { label: '未知', animation: 'none' },\n};\n\n/** 管材配色(maplibre style 用) */\nexport const PIPE_MATERIAL_COLORS: Record<PipeMaterial, string> = {\n cast_iron: '#94a3b8', // 蓝灰(旧铸铁)\n ductile_iron: '#0ea5e9', // 天蓝(球墨铸铁)\n steel: '#64748b', // 钢蓝\n pe: '#22c55e', // 绿(PE 现代管材)\n pvc: '#a78bfa', // 紫(PVC)\n concrete: '#a8a29e', // 米灰(混凝土)\n hdpe: '#16a34a', // 深绿(HDPE)\n copper: '#d97706', // 橙(铜)\n unknown: '#6b7280',\n};\n\n/** 节点图标(用 SVG path 描点,地图渲染层用) */\nexport const NODE_KIND_ICONS: Record<NodeKind, string> = {\n junction: 'M -6 0 L 6 0 M 0 -6 L 0 6', // 十字\n valve: 'M -8 -4 L 8 4 M 0 -4 L 0 4', // X\n pump: 'M -6 0 A 6 6 0 1 0 6 0 A 6 6 0 1 0 -6 0', // 圆\n meter: 'M -6 -6 L 6 -6 L 6 6 L -6 6 Z', // 方\n source: 'M 0 -10 L 6 6 L -6 6 Z', // 三角\n tank: 'M -8 -6 Q 0 -10 8 -6 L 8 6 L -8 6 Z', // 圆角\n junction_box: 'M -7 -4 L 7 -4 L 7 4 L -7 4 Z',// 小方\n};\n\n/** 节点配色(默认按 type 衍生) */\nexport const NODE_KIND_COLORS: Record<NodeKind, string> = {\n junction: '#94a3b8',\n valve: '#ef4444', // 阀门=红(控制关键)\n pump: '#f59e0b',\n meter: '#3b82f6',\n source: '#22c55e',\n tank: '#06b6d4',\n junction_box: '#8b5cf6',\n};\n\n/** 健康度等级配色(PipelineHealth 用) */\nexport const HEALTH_LEVEL_COLORS: Record<string, string> = {\n excellent: '#22c55e', // 绿 80-100\n good: '#3b82f6', // 蓝 60-80\n fair: '#eab308', // 黄 40-60\n poor: '#f97316', // 橙 20-40\n critical: '#ef4444', // 红 0-20\n};\n\n/** 健康等级 label */\nexport const HEALTH_LEVEL_LABELS: Record<string, string> = {\n excellent: '优',\n good: '良',\n fair: '中',\n poor: '差',\n critical: '危',\n};\n\n/**\n * 健康度 → 等级(保留闭区间,遵守 PRD §4.4.2)\n * @param score 0-100 健康分\n */\nexport function healthLevel(score: number): 'excellent' | 'good' | 'fair' | 'poor' | 'critical' {\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\n/** 按管线类型取默认色(兜底用) */\nexport function pipelineTypeColor(type: PipelineType | undefined): string {\n if (!type) return '#94a3b8';\n return PIPELINE_TYPE_COLORS[type];\n}\n","/**\n * 管网 MapLibre paint 规则(caoguo-pipeline)\n *\n * 把\"管线/节点如何着色\"的业务规则翻译成 MapLibre style spec,\n * 业务方可直接 addLayer / setPaintProperty 使用。\n *\n * 规则集:\n * - 管线按管线大类着色(type)\n * - 管线按管径着色(diameter,PRD §4.1.4)\n * - 管线按状态着色(status)\n * - 管线按材质着色(material)\n * - 管线按健康度着色(health,PipelineHealth 集成)\n *\n * 节点按节点类型着色 + 图标。\n */\n\nimport type {\n PipelineType,\n PipeMaterial,\n PipeStatus,\n ColorByMode,\n} from '../types';\nimport { PIPELINE_TYPE_COLORS, PIPE_STATUS_COLORS, HEALTH_LEVEL_COLORS, PIPE_MATERIAL_COLORS } from './pipelineTheme';\n\n/**\n * MapLibre style spec 的 color function DSL(typed),\n * 业务方可直接 `map.addLayer({type:'line', paint: {'line-color': rule} })`\n *\n * MapLibre 表达式其实接受 string | number | 嵌套数组,所以我们用 unknown 让 TS 放行\n */\nexport type PaintRule = unknown;\n\n/** 管线按\"管线大类\"着色 */\nexport function paintPipeByType(types?: PipelineType[]): PaintRule {\n if (!types || types.length === 0) {\n // 全部类型都涂色\n return [\n 'match',\n ['get', 'pipelineType'],\n ...Object.entries(PIPELINE_TYPE_COLORS).flatMap(([k, v]) => [k, v]),\n '#94a3b8',\n ];\n }\n return [\n 'match',\n ['get', 'pipelineType'],\n ...types.flatMap((t) => [t, PIPELINE_TYPE_COLORS[t]]),\n '#94a3b8',\n ];\n}\n\n/** 管线按\"管径\"渐变着色(PRD §4.1.4) */\nexport function paintPipeByDiameter(): PaintRule {\n return [\n 'interpolate',\n ['linear'],\n ['coalesce', ['get', 'diameter'], 0],\n 0, '#4ade80', // 极细管 绿\n 50, '#4ade80', // DN50 以下 绿(支管)\n 150, '#60a5fa', // DN150 蓝(配水管)\n 300, '#f59e0b', // DN300 黄(干管)\n 600, '#ef4444', // DN600+ 红(主管)\n 1500, '#7f1d1d', // 极大管 深红\n ];\n}\n\n/** 管线按\"状态\"着色 */\nexport function paintPipeByStatus(): PaintRule {\n return [\n 'match',\n ['coalesce', ['get', 'status'], 'unknown'],\n ...Object.entries(PIPE_STATUS_COLORS).flatMap(([k, v]) => [k, v]),\n '#6b7280',\n ];\n}\n\n/** 管线按\"材质\"着色 */\nexport function paintPipeByMaterial(): PaintRule {\n return [\n 'match',\n ['coalesce', ['get', 'material'], 'unknown'],\n ...Object.entries(PIPE_MATERIAL_COLORS).flatMap(([k, v]) => [k, v]),\n '#6b7280',\n ];\n}\n\n/** 管线按\"健康度\"着色(健康分 0-100,规则按 healthLevel 离散) */\nexport function paintPipeByHealth(): PaintRule {\n return [\n 'step',\n ['coalesce', ['get', 'healthScore'], 100],\n HEALTH_LEVEL_COLORS.critical, // <20\n 20, HEALTH_LEVEL_COLORS.poor,\n 40, HEALTH_LEVEL_COLORS.fair,\n 60, HEALTH_LEVEL_COLORS.good,\n 80, HEALTH_LEVEL_COLORS.excellent,\n ];\n}\n\n/** 根据模式返回 paint rule 工厂 */\nexport function paintPipeBy(mode: ColorByMode, opts?: { types?: PipelineType[] }): PaintRule {\n switch (mode) {\n case 'type':\n return paintPipeByType(opts?.types);\n case 'diameter':\n return paintPipeByDiameter();\n case 'status':\n return paintPipeByStatus();\n case 'material':\n return paintPipeByMaterial();\n case 'health':\n return paintPipeByHealth();\n case 'uniform':\n default:\n return '#60a5fa';\n }\n}\n\n/** 节点按 kind 着色(maplibre style circle-color 用) */\nexport function paintNodeByKind(): PaintRule {\n return [\n 'match',\n ['get', 'kind'],\n 'junction', '#94a3b8',\n 'valve', '#ef4444',\n 'pump', '#f59e0b',\n 'meter', '#3b82f6',\n 'source', '#22c55e',\n 'tank', '#06b6d4',\n 'junction_box', '#8b5cf6',\n '#cbd5e1',\n ];\n}\n\n/** 默认管段宽度(按管径递推) */\nexport function paintPipeWidthByDiameter(minWidth = 1.5, maxWidth = 6): PaintRule {\n return [\n 'interpolate',\n ['linear'],\n ['coalesce', ['get', 'diameter'], 100],\n 0, minWidth,\n 1500, maxWidth,\n ];\n}\n","/**\n * PipelineTopology 类(Phase 1 管网拓扑编辑器)\n *\n * 完整实现:\n * - 拓扑渲染(管段/节点按类型着色)\n * - 层级钻取(区域→街道→小区→楼栋,drillDown / drillUp)\n * - 设备卡片(点击节点/管段触发 onNodeSelect / onPipeSelect)\n * - 连通性高亮(选中管线后高亮上下游,highlightConnectivity)\n * - 搜索定位(按编号/地址/区域,search)\n * - 分层控制(按管径/材质/年代/状态,setLayerFilter)\n */\n\nimport type { Map as CaoguoMap } from '@caoguo/maplibre';\nimport { upsertSource } from '@caoguo/maplibre';\nimport type {\n PipelineTopologyDataset,\n ColorByMode,\n PipelineType,\n PipelineNode,\n PipelinePipe,\n NodeSelectEvent,\n PipeSelectEvent,\n DrillDownEvent,\n} from '../types';\nimport { paintPipeBy } from '../style/paintRules';\n\nexport interface PipelineTopologyOptions {\n map: CaoguoMap;\n dataset: PipelineTopologyDataset;\n /** 默认着色模式 */\n colorBy?: ColorByMode;\n /** 仅显示指定管线类型(空 = 全显示) */\n pipelineTypes?: PipelineType[];\n /** 层 ID 前缀 */\n layerPrefix?: string;\n}\n\ntype NodeListener = (e: NodeSelectEvent) => void;\ntype PipeListener = (e: PipeSelectEvent) => void;\ntype DrillListener = (e: DrillDownEvent) => void;\n\n/** 分层控制过滤器(按管径/材质/状态/年代) */\nexport interface LayerFilter {\n /** 最小管径(mm) */\n minDiameter?: number;\n /** 最大管径(mm) */\n maxDiameter?: number;\n /** 管材 */\n material?: string;\n /** 状态 */\n status?: string;\n /** 最小使用年限 */\n minAgeYears?: number;\n}\n\n/** 搜索结果 */\nexport interface SearchResult {\n /** 匹配节点 */\n nodes: PipelineNode[];\n /** 匹配管段 */\n pipes: PipelinePipe[];\n}\n\n/**\n * PipelineTopology 组件(Phase 1 MVP 完整实现)\n *\n * 实现进度:\n * - [x] 渲染数据接入\n * - [x] 层级钻取(drillDown / drillUp)\n * - [x] 设备卡片(点击绑定 onNodeSelect / onPipeSelect)\n * - [x] 连通性高亮(highlightConnectivity)\n * - [x] 搜索定位(search)\n * - [x] 分层控制(setLayerFilter / clearLayerFilter)\n */\nexport class PipelineTopology {\n private map: CaoguoMap;\n private dataset: PipelineTopologyDataset;\n private colorBy: ColorByMode;\n private layerPrefix: string;\n private pipelineTypes?: PipelineType[];\n private layerIds: string[] = [];\n private nodeListeners = new Set<NodeListener>();\n private pipeListeners = new Set<PipeListener>();\n private drillListeners = new Set<DrillListener>();\n /** 当前钻取区域(null = 全量) */\n private currentRegion: string | null = null;\n /** 当前分层过滤器 */\n private layerFilter: LayerFilter | null = null;\n /** click 事件是否已绑定 */\n private clickBound = false;\n\n constructor(options: PipelineTopologyOptions) {\n this.map = options.map;\n this.dataset = options.dataset;\n this.colorBy = options.colorBy ?? 'type';\n this.pipelineTypes = options.pipelineTypes;\n this.layerPrefix = options.layerPrefix ?? 'cg-topo';\n }\n\n /** 渲染管段+节点到地图 */\n render(): 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 on: (event: string, layerId: string, cb: (e: unknown) => void) => void;\n };\n }).instance;\n const prefix = this.layerPrefix;\n\n const pipeGeoJSON: GeoJSON.FeatureCollection<GeoJSON.LineString> = {\n type: 'FeatureCollection',\n features: this.dataset.pipes\n .filter((p) => this.isPipeVisible(p))\n .flatMap((p) => {\n const from = this.dataset.nodes.find((n) => n.id === p.fromNode);\n const to = this.dataset.nodes.find((n) => n.id === p.toNode);\n if (!from || !to) return [];\n const coords: [number, number][] =\n p.geometry && p.geometry.length >= 2\n ? (p.geometry as [number, number][])\n : [\n [from.lng, from.lat],\n [to.lng, to.lat],\n ];\n return [\n {\n type: 'Feature' as const,\n geometry: { type: 'LineString' as const, coordinates: coords },\n properties: {\n pipeId: p.id,\n pipelineType: p.pipelineType ?? '',\n diameter: p.properties?.diameter ?? 0,\n status: p.properties?.status ?? 'unknown',\n material: p.properties?.material ?? 'unknown',\n },\n },\n ];\n }),\n };\n\n const nodeGeoJSON: GeoJSON.FeatureCollection<GeoJSON.Point> = {\n type: 'FeatureCollection',\n features: this.dataset.nodes\n .filter((n) => this.isNodeVisible(n))\n .map((n) => ({\n type: 'Feature' as const,\n geometry: { type: 'Point' as const, coordinates: [n.lng, n.lat] },\n properties: { nodeId: n.id, kind: n.kind, pipelineType: n.pipelineType ?? '' },\n })),\n };\n\n // 幂等:层级/着色切换重渲染时 source 可能已存在,先判断避免抛 \"already exists\"\n upsertSource(mlMap, `${prefix}-pipes-src`, pipeGeoJSON);\n upsertSource(mlMap, `${prefix}-nodes-src`, nodeGeoJSON);\n mlMap.addLayer({\n id: `${prefix}-pipes-line`,\n type: 'line',\n source: `${prefix}-pipes-src`,\n paint: {\n 'line-color': paintPipeBy(this.colorBy, { types: this.pipelineTypes }) as never,\n 'line-width': 2,\n 'line-opacity': 0.85,\n },\n });\n mlMap.addLayer({\n id: `${prefix}-nodes-pt`,\n type: 'circle',\n source: `${prefix}-nodes-src`,\n paint: {\n 'circle-radius': 4,\n 'circle-color': '#f59e0b',\n 'circle-stroke-width': 1.5,\n 'circle-stroke-color': '#ffffff',\n },\n });\n this.layerIds.push(`${prefix}-pipes-line`, `${prefix}-nodes-pt`);\n this.bindClickEvents(mlMap, prefix);\n }\n\n /**\n * 绑定点击事件(设备卡片:点击节点/管段触发订阅回调)。\n * 只绑定一次(render 重建图层后事件仍指向固定 layerId)。\n */\n private bindClickEvents(\n mlMap: {\n on: (event: string, layerId: string, cb: (e: unknown) => void) => void;\n },\n prefix: string\n ): void {\n if (this.clickBound) return;\n this.clickBound = true;\n\n const nodeById = new Map(this.dataset.nodes.map((n) => [n.id, n] as const));\n const pipeById = new Map(this.dataset.pipes.map((p) => [p.id, p] as const));\n\n mlMap.on('click', `${prefix}-pipes-line`, (e) => {\n const features = (e as { features?: Array<{ properties?: { pipeId?: string } }> }).features ?? [];\n const f = features[0];\n const pipe = f?.properties?.pipeId ? pipeById.get(f.properties.pipeId) : undefined;\n if (pipe) {\n for (const l of this.pipeListeners) l({ pipe });\n }\n });\n\n mlMap.on('click', `${prefix}-nodes-pt`, (e) => {\n const features = (e as { features?: Array<{ properties?: { nodeId?: string } }> }).features ?? [];\n const f = features[0];\n const node = f?.properties?.nodeId ? nodeById.get(f.properties.nodeId) : undefined;\n if (node) {\n for (const l of this.nodeListeners) l({ node });\n }\n });\n }\n\n /** 切换着色模式 */\n setColorBy(mode: ColorByMode): void {\n this.colorBy = mode;\n const mlMap = (this.map as unknown as {\n instance: { setPaintProperty: (id: string, prop: string, value: unknown) => void };\n }).instance;\n if (mlMap.setPaintProperty) {\n try {\n mlMap.setPaintProperty(\n `${this.layerPrefix}-pipes-line`,\n 'line-color',\n paintPipeBy(mode, { types: this.pipelineTypes }) as never\n );\n } catch {\n // ignore\n }\n }\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.nodeListeners.clear();\n this.pipeListeners.clear();\n this.drillListeners.clear();\n }\n\n /** 订阅节点点击事件 */\n onNodeSelect(fn: NodeListener): () => void {\n this.nodeListeners.add(fn);\n return () => this.nodeListeners.delete(fn);\n }\n\n /** 订阅管段点击事件 */\n onPipeSelect(fn: PipeListener): () => void {\n this.pipeListeners.add(fn);\n return () => this.pipeListeners.delete(fn);\n }\n\n /** 订阅钻取事件 */\n onDrillDown(fn: DrillListener): () => void {\n this.drillListeners.add(fn);\n return () => this.drillListeners.delete(fn);\n }\n\n /** 管段是否可见(管线类型 + 区域钻取 + 分层过滤) */\n private isPipeVisible(p: PipelinePipe): boolean {\n if (this.pipelineTypes?.length && !this.pipelineTypes.includes(p.pipelineType as PipelineType)) {\n return false;\n }\n if (this.currentRegion && p.region !== this.currentRegion) return false;\n const f = this.layerFilter;\n if (f) {\n const props = p.properties ?? {};\n if (f.minDiameter !== undefined && (props.diameter ?? 0) < f.minDiameter) return false;\n if (f.maxDiameter !== undefined && (props.diameter ?? Infinity) > f.maxDiameter) return false;\n if (f.material && props.material !== f.material) return false;\n if (f.status && props.status !== f.status) return false;\n if (f.minAgeYears !== undefined && props.installDate) {\n if (this.ageInYears(props.installDate) < f.minAgeYears) return false;\n }\n }\n return true;\n }\n\n /** 节点是否可见(管线类型 + 区域钻取) */\n private isNodeVisible(n: PipelineNode): boolean {\n if (this.pipelineTypes?.length && !this.pipelineTypes.includes(n.pipelineType as PipelineType)) {\n return false;\n }\n if (this.currentRegion && n.region !== this.currentRegion) return false;\n return true;\n }\n\n /** 计算管段使用年限(年) */\n private ageInYears(installDate: string): number {\n const d = new Date(installDate).getTime();\n if (Number.isNaN(d)) return 0;\n return (Date.now() - d) / (365.25 * 24 * 3600 * 1000);\n }\n\n /**\n * 搜索定位(按编号/地址/区域/类型)。\n * 返回匹配的节点与管段,供上层 flyTo 定位。\n */\n search(query: string): SearchResult {\n const q = query.trim().toLowerCase();\n if (!q) return { nodes: [], pipes: [] };\n const nodes = this.dataset.nodes.filter(\n (n) =>\n n.id.toLowerCase().includes(q) ||\n (n.properties?.code ?? '').toLowerCase().includes(q) ||\n (n.region ?? '').toLowerCase().includes(q) ||\n n.kind.toLowerCase().includes(q)\n );\n const pipes = this.dataset.pipes.filter(\n (p) =>\n p.id.toLowerCase().includes(q) ||\n (p.region ?? '').toLowerCase().includes(q) ||\n (p.properties?.material ?? '').toLowerCase().includes(q)\n );\n return { nodes, pipes };\n }\n\n /** 层级钻取:下钻到指定区域(过滤渲染 + 触发事件) */\n drillDown(region: string): void {\n const from = this.currentRegion;\n this.currentRegion = region;\n this.render();\n for (const l of this.drillListeners) l({ from, to: region });\n }\n\n /** 层级钻取:返回上一级(清空区域过滤) */\n drillUp(): void {\n const from = this.currentRegion;\n this.currentRegion = null;\n this.render();\n for (const l of this.drillListeners) l({ from, to: '' });\n }\n\n /** 分层控制:按管径/材质/状态/年代过滤 */\n setLayerFilter(filter: LayerFilter): void {\n this.layerFilter = filter;\n this.render();\n }\n\n /** 清空分层过滤器 */\n clearLayerFilter(): void {\n this.layerFilter = null;\n this.render();\n }\n\n /** 高亮连通路径(基于节点 ID) */\n highlightConnectivity(centerId: string): Set<string> {\n const adj = new Map<string, Set<string>>();\n for (const p of this.dataset.pipes) {\n if (!adj.has(p.fromNode)) adj.set(p.fromNode, new Set());\n if (!adj.has(p.toNode)) adj.set(p.toNode, new Set());\n adj.get(p.fromNode)!.add(p.toNode);\n adj.get(p.toNode)!.add(p.fromNode);\n }\n\n const visited = new Set<string>();\n const stack = [centerId];\n while (stack.length > 0) {\n const cur = stack.pop()!;\n if (visited.has(cur)) continue;\n visited.add(cur);\n const neighbors = adj.get(cur) ?? new Set();\n for (const n of neighbors) {\n if (!visited.has(n)) stack.push(n);\n }\n }\n return visited;\n }\n}\n"]}