@caoguo/maplibre-pipeline 0.0.6 → 0.0.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.cts CHANGED
@@ -5,6 +5,6 @@ export { PipelineTopology, PipelineTopologyOptions } from './topology/index.cjs'
5
5
  export { B as BurstResult, a as BurstSimulateOptions, b as BurstSimulateResult, c as BurstSimulator, d as BurstSimulatorOptions, I as ImpactArea, V as ValvePlan, e as convexHull, s as simulateBurst, u as userSeverity } from './burstClass-CGMjcAYy.cjs';
6
6
  export { CandidateValveSet, MapLayerData, PlannerOptions, evaluateValvePlan, impactAreaSquareMeters, impactCenter, listCandidateValves, nearestNodeOnPipe, toMapLayerData } from './burst/index.cjs';
7
7
  export { ConcentrationContour, DemGrid, DispersionCoefficients, FloodParams, FloodResult, GasLeakParams, GasLeakResult, LeakagePlume, LeakagePlumeOptions, PasquillGiffordParams, StabilityClass, classifyFromMeteo, classifyStability, dispersionCoefficients, gaussianPlume, simulateFlood } from './leakage/index.cjs';
8
- export { DEFAULT_WEIGHTS, HealthResult, HeatmapCell, PipeHealthInput, PipeHealthPoint, PipeHealthScore, PipelineHealth, PipelineHealthOptions, RiskHeatmapOptions, aggregateHeatmap, heatmapToGeoJSON, prioritizeMaintenance, scorePipeHealth, scorePipes } from './health/index.cjs';
8
+ export { DEFAULT_WEIGHTS, HealthCard, HealthResult, HeatmapCell, PipeHealthInput, PipeHealthPoint, PipeHealthScore, PipelineHealth, PipelineHealthOptions, RiskHeatmapOptions, aggregateHeatmap, buildHealthCard, buildHealthScoreSeries, heatmapToGeoJSON, prioritizeMaintenance, scorePipeHealth, scorePipes } from './health/index.cjs';
9
9
  export { PipelineNlp, PipelineNlpFilters, PipelineNlpIntent, PipelineNlpOptions, PipelineNlpResult, parsePipelineQuery } from './nlpg/index.cjs';
10
10
  import '@caoguo/maplibre';
package/dist/index.d.ts CHANGED
@@ -5,6 +5,6 @@ export { PipelineTopology, PipelineTopologyOptions } from './topology/index.js';
5
5
  export { B as BurstResult, a as BurstSimulateOptions, b as BurstSimulateResult, c as BurstSimulator, d as BurstSimulatorOptions, I as ImpactArea, V as ValvePlan, e as convexHull, s as simulateBurst, u as userSeverity } from './burstClass-Cf2p71LE.js';
6
6
  export { CandidateValveSet, MapLayerData, PlannerOptions, evaluateValvePlan, impactAreaSquareMeters, impactCenter, listCandidateValves, nearestNodeOnPipe, toMapLayerData } from './burst/index.js';
7
7
  export { ConcentrationContour, DemGrid, DispersionCoefficients, FloodParams, FloodResult, GasLeakParams, GasLeakResult, LeakagePlume, LeakagePlumeOptions, PasquillGiffordParams, StabilityClass, classifyFromMeteo, classifyStability, dispersionCoefficients, gaussianPlume, simulateFlood } from './leakage/index.js';
8
- export { DEFAULT_WEIGHTS, HealthResult, HeatmapCell, PipeHealthInput, PipeHealthPoint, PipeHealthScore, PipelineHealth, PipelineHealthOptions, RiskHeatmapOptions, aggregateHeatmap, heatmapToGeoJSON, prioritizeMaintenance, scorePipeHealth, scorePipes } from './health/index.js';
8
+ export { DEFAULT_WEIGHTS, HealthCard, HealthResult, HeatmapCell, PipeHealthInput, PipeHealthPoint, PipeHealthScore, PipelineHealth, PipelineHealthOptions, RiskHeatmapOptions, aggregateHeatmap, buildHealthCard, buildHealthScoreSeries, heatmapToGeoJSON, prioritizeMaintenance, scorePipeHealth, scorePipes } from './health/index.js';
9
9
  export { PipelineNlp, PipelineNlpFilters, PipelineNlpIntent, PipelineNlpOptions, PipelineNlpResult, parsePipelineQuery } from './nlpg/index.js';
10
10
  import '@caoguo/maplibre';
package/dist/index.js CHANGED
@@ -4,7 +4,7 @@ export { detectCycles, dfs } from './chunk-CDEYJ67B.js';
4
4
  export { BurstSimulator, convexHull, evaluateValvePlan, impactAreaSquareMeters, impactCenter, listCandidateValves, nearestNodeOnPipe, simulateBurst, toMapLayerData, userSeverity } from './chunk-2O4IXAXO.js';
5
5
  export { bfs, buildAdjacency, dijkstra, edgesOf, findConnectedComponents, findPaths, findUpstreamNode, haversine, highlightConnectivity, multiSourceBfs, neighborIds, pipeLengthFromGeometry, reconstructPath } from './chunk-XCP7XVNO.js';
6
6
  export { LeakagePlume, classifyFromMeteo, classifyStability, dispersionCoefficients, gaussianPlume, simulateFlood } from './chunk-VSTG2ZUY.js';
7
- export { DEFAULT_WEIGHTS, PipelineHealth, aggregateHeatmap, heatmapToGeoJSON, prioritizeMaintenance, scorePipeHealth, scorePipes } from './chunk-3656CGXE.js';
7
+ export { DEFAULT_WEIGHTS, PipelineHealth, aggregateHeatmap, buildHealthCard, buildHealthScoreSeries, heatmapToGeoJSON, prioritizeMaintenance, scorePipeHealth, scorePipes } from './chunk-33TITD2I.js';
8
8
  export { PipelineTopology } from './chunk-ASITLG37.js';
9
9
  export { HEALTH_LEVEL_COLORS, HEALTH_LEVEL_LABELS, INDUSTRY_PRIMARY, NODE_KIND_COLORS, NODE_KIND_ICONS, PIPELINE_TYPE_COLORS, PIPELINE_TYPE_ICONS, PIPELINE_TYPE_LABELS, PIPE_MATERIAL_COLORS, PIPE_STATUS_COLORS, PIPE_STATUS_META, healthLevel, paintNodeByKind, paintPipeBy, paintPipeByDiameter, paintPipeByHealth, paintPipeByMaterial, paintPipeByStatus, paintPipeByType, paintPipeWidthByDiameter, pipelineTypeColor } from './chunk-4HP7Q7OL.js';
10
10
  export { PipelineNlp, parsePipelineQuery } from './chunk-7LPD52KL.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@caoguo/maplibre-pipeline",
3
- "version": "0.0.6",
3
+ "version": "0.0.7",
4
4
  "description": "草果地图管网组件包 - 爆管推演 / 泄漏扩散 / 健康评估 / 拓扑编辑器",
5
5
  "license": "Apache-2.0",
6
6
  "author": "caoguo <caoguo@hb.cn>",
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/health/healthScorer.ts","../src/health/riskHeatmap.ts","../src/health/PipelineHealth.ts"],"names":[],"mappings":";AAiDO,IAAM,eAAA,GAAkB;AAAA,EAC7B,GAAA,EAAK,IAAA;AAAA,EACL,QAAA,EAAU,GAAA;AAAA,EACV,IAAA,EAAM,IAAA;AAAA,EACN,OAAA,EAAS,GAAA;AAAA,EACT,QAAA,EAAU,GAAA;AAAA,EACV,UAAA,EAAY;AACd;AAEA,IAAM,aAAA,GAA8C;AAAA,EAClD,SAAA,EAAW,IAAA;AAAA;AAAA,EACX,YAAA,EAAc,GAAA;AAAA;AAAA,EACd,KAAA,EAAO,GAAA;AAAA,EACP,EAAA,EAAI,GAAA;AAAA,EACJ,GAAA,EAAK,IAAA;AAAA,EACL,QAAA,EAAU,GAAA;AAAA,EACV,IAAA,EAAM,IAAA;AAAA,EACN,MAAA,EAAQ,GAAA;AAAA,EACR,OAAA,EAAS;AACX,CAAA;AAKO,SAAS,eAAA,CAAgB,KAAA,EAAwB,OAAA,GAAU,eAAA,EAAkC;AAClG,EAAA,MAAM,IAAA,GAAO;AAAA,IACX,GAAA,EAAK,QAAA,CAAS,KAAA,EAAO,OAAA,CAAQ,GAAG,CAAA;AAAA,IAChC,QAAA,EAAU,aAAA,CAAc,KAAA,EAAO,OAAA,CAAQ,QAAQ,CAAA;AAAA,IAC/C,IAAA,EAAM,SAAA,CAAU,KAAA,EAAO,OAAA,CAAQ,IAAI,CAAA;AAAA,IACnC,OAAA,EAAS,YAAA,CAAa,KAAA,EAAO,OAAA,CAAQ,OAAO,CAAA;AAAA,IAC5C,QAAA,EAAU,aAAA,CAAc,KAAA,EAAO,OAAA,CAAQ,QAAQ,CAAA;AAAA,IAC/C,UAAA,EAAY,eAAA,CAAgB,KAAA,EAAO,OAAA,CAAQ,UAAU;AAAA,GACvD;AAEA,EAAA,MAAM,KAAA,GACJ,IAAA,CAAK,GAAA,CAAI,KAAA,GAAQ,KAAK,GAAA,CAAI,MAAA,GAC1B,IAAA,CAAK,QAAA,CAAS,QAAQ,IAAA,CAAK,QAAA,CAAS,MAAA,GACpC,IAAA,CAAK,KAAK,KAAA,GAAQ,IAAA,CAAK,IAAA,CAAK,MAAA,GAC5B,KAAK,OAAA,CAAQ,KAAA,GAAQ,IAAA,CAAK,OAAA,CAAQ,SAClC,IAAA,CAAK,QAAA,CAAS,KAAA,GAAQ,IAAA,CAAK,SAAS,MAAA,GACpC,IAAA,CAAK,UAAA,CAAW,KAAA,GAAQ,KAAK,UAAA,CAAW,MAAA;AAG1C,EAAA,IAAI,KAAA,GAAQ,KAAA;AACZ,EAAA,IAAI,KAAA,CAAM,MAAA,KAAW,SAAA,EAAW,KAAA,IAAS,GAAA;AAAA,OAAA,IAChC,KAAA,CAAM,MAAA,KAAW,cAAA,EAAgB,KAAA,IAAS,GAAA;AAAA,OAAA,IAC1C,KAAA,CAAM,MAAA,KAAW,WAAA,EAAa,KAAA,GAAQ,CAAA;AAE/C,EAAA,OAAO;AAAA,IACL,KAAA,EAAO,IAAA,CAAK,KAAA,CAAM,KAAK,CAAA;AAAA,IACvB,KAAA,EAAO,QAAQ,KAAK,CAAA;AAAA,IACpB,UAAA,EAAY;AAAA,GACd;AACF;AAEA,SAAS,QAAQ,KAAA,EAAyC;AACxD,EAAA,IAAI,KAAA,IAAS,IAAI,OAAO,WAAA;AACxB,EAAA,IAAI,KAAA,IAAS,IAAI,OAAO,MAAA;AACxB,EAAA,IAAI,KAAA,IAAS,IAAI,OAAO,MAAA;AACxB,EAAA,IAAI,KAAA,IAAS,IAAI,OAAO,MAAA;AACxB,EAAA,OAAO,UAAA;AACT;AAEA,SAAS,QAAA,CAAS,OAAwB,MAAA,EAAsD;AAC9F,EAAA,IAAI,CAAC,MAAM,WAAA,EAAa;AACtB,IAAA,OAAO,EAAE,KAAA,EAAO,EAAA,EAAI,MAAA,EAAQ,QAAQ,oEAAA,EAAc;AAAA,EACpD;AACA,EAAA,MAAM,QAAA,GAAA,CAAY,IAAA,CAAK,GAAA,EAAI,GAAI,IAAI,IAAA,CAAK,KAAA,CAAM,WAAW,CAAA,CAAE,OAAA,EAAQ,KAAM,MAAA,GAAS,KAAK,IAAA,GAAO,GAAA,CAAA;AAC9F,EAAA,IAAI,QAAA,GAAW,GAAG,OAAO,EAAE,OAAO,GAAA,EAAK,MAAA,EAAQ,QAAQ,wDAAA,EAAY;AACnE,EAAA,IAAI,QAAA,GAAW,GAAG,OAAO,EAAE,OAAO,GAAA,EAAK,MAAA,EAAQ,QAAQ,qCAAA,EAAa;AACpE,EAAA,IAAI,QAAA,GAAW,IAAI,OAAO,EAAE,OAAO,EAAA,EAAI,MAAA,EAAQ,QAAQ,2BAAA,EAAa;AACpE,EAAA,IAAI,QAAA,GAAW,IAAI,OAAO,EAAE,OAAO,EAAA,EAAI,MAAA,EAAQ,QAAQ,4BAAA,EAAc;AACrE,EAAA,IAAI,QAAA,GAAW,IAAI,OAAO,EAAE,OAAO,EAAA,EAAI,MAAA,EAAQ,QAAQ,4BAAA,EAAc;AACrE,EAAA,IAAI,QAAA,GAAW,IAAI,OAAO,EAAE,OAAO,EAAA,EAAI,MAAA,EAAQ,QAAQ,0BAAA,EAAY;AACnE,EAAA,OAAO,EAAE,KAAA,EAAO,CAAA,EAAG,MAAA,EAAQ,QAAQ,0BAAA,EAAY;AACjD;AAEA,SAAS,aAAA,CAAc,OAAwB,MAAA,EAA2D;AACxG,EAAA,IAAI,CAAC,MAAM,QAAA,EAAU;AACnB,IAAA,OAAO,EAAE,KAAA,EAAO,EAAA,EAAI,MAAA,EAAQ,QAAQ,wDAAA,EAAY;AAAA,EAClD;AACA,EAAA,MAAM,IAAA,GAAO,aAAA,CAAc,KAAA,CAAM,QAAQ,CAAA,IAAK,GAAA;AAC9C,EAAA,MAAM,KAAA,GAAQ,IAAA,CAAK,KAAA,CAAA,CAAO,CAAA,GAAI,QAAQ,GAAG,CAAA;AACzC,EAAA,MAAM,MAAA,GAAiC;AAAA,IACrC,SAAA,EAAW,4CAAA;AAAA,IACX,YAAA,EAAc,0BAAA;AAAA,IACd,KAAA,EAAO,QAAA;AAAA,IACP,EAAA,EAAI,kCAAA;AAAA,IACJ,GAAA,EAAK,KAAA;AAAA,IACL,QAAA,EAAU,oBAAA;AAAA,IACV,IAAA,EAAM,8BAAA;AAAA,IACN,MAAA,EAAQ,QAAA;AAAA,IACR,OAAA,EAAS;AAAA,GACX;AACA,EAAA,OAAO;AAAA,IACL,KAAA;AAAA,IACA,MAAA;AAAA,IACA,MAAA,EAAQ,MAAA,CAAO,KAAA,CAAM,QAAQ,KAAK,KAAA,CAAM;AAAA,GAC1C;AACF;AAEA,SAAS,SAAA,CAAU,OAAwB,MAAA,EAAuD;AAChG,EAAA,MAAM,CAAA,GAAI,MAAM,aAAA,IAAiB,GAAA;AACjC,EAAA,MAAM,KAAA,GAAQ,IAAA,CAAK,KAAA,CAAA,CAAO,CAAA,GAAI,KAAK,GAAG,CAAA;AACtC,EAAA,OAAO;AAAA,IACL,KAAA;AAAA,IACA,MAAA;AAAA,IACA,MAAA,EAAQ,CAAA,KAAM,GAAA,IAAO,CAAC,KAAA,CAAM,aAAA,GAAgB,8DAAA,GAAe,CAAA,qCAAA,EAAU,CAAA,CAAE,OAAA,CAAQ,CAAC,CAAC,CAAA;AAAA,GACnF;AACF;AAEA,SAAS,YAAA,CAAa,OAAwB,MAAA,EAA0D;AACtG,EAAA,MAAM,IAAA,GAAO,MAAM,YAAA,IAAgB,CAAA;AACnC,EAAA,IAAI,IAAA,KAAS,GAAG,OAAO,EAAE,OAAO,GAAA,EAAK,MAAA,EAAQ,QAAQ,gCAAA,EAAQ;AAC7D,EAAA,IAAI,IAAA,KAAS,GAAG,OAAO,EAAE,OAAO,EAAA,EAAI,MAAA,EAAQ,QAAQ,mCAAA,EAAW;AAC/D,EAAA,IAAI,IAAA,KAAS,GAAG,OAAO,EAAE,OAAO,EAAA,EAAI,MAAA,EAAQ,QAAQ,mCAAA,EAAW;AAC/D,EAAA,IAAI,IAAA,KAAS,GAAG,OAAO,EAAE,OAAO,EAAA,EAAI,MAAA,EAAQ,QAAQ,mCAAA,EAAW;AAC/D,EAAA,OAAO,EAAE,KAAA,EAAO,CAAA,EAAG,QAAQ,MAAA,EAAQ,CAAA,mBAAA,EAAO,IAAI,CAAA,mBAAA,CAAA,EAAO;AACvD;AAEA,SAAS,aAAA,CAAc,OAAwB,MAAA,EAA2D;AACxG,EAAA,IAAI,CAAC,MAAM,QAAA,EAAU;AACnB,IAAA,OAAO,EAAE,KAAA,EAAO,EAAA,EAAI,MAAA,EAAQ,QAAQ,0EAAA,EAAe;AAAA,EACrD;AACA,EAAA,IAAI,CAAC,MAAM,aAAA,EAAe;AACxB,IAAA,OAAO,EAAE,KAAA,EAAO,EAAA,EAAI,MAAA,EAAQ,QAAQ,sCAAA,EAAS;AAAA,EAC/C;AACA,EAAA,MAAM,KAAA,GAAQ,KAAA,CAAM,QAAA,GAAW,KAAA,CAAM,aAAA;AACrC,EAAA,IAAI,KAAA,GAAQ,KAAK,OAAO,EAAE,OAAO,EAAA,EAAI,MAAA,EAAQ,QAAQ,oDAAA,EAAkB;AACvE,EAAA,IAAI,KAAA,GAAQ,KAAK,OAAO,EAAE,OAAO,EAAA,EAAI,MAAA,EAAQ,QAAQ,sDAAA,EAAoB;AACzE,EAAA,IAAI,KAAA,GAAQ,GAAK,OAAO,EAAE,OAAO,EAAA,EAAI,MAAA,EAAQ,QAAQ,uDAAA,EAAqB;AAC1E,EAAA,IAAI,KAAA,GAAQ,KAAK,OAAO,EAAE,OAAO,EAAA,EAAI,MAAA,EAAQ,QAAQ,qCAAA,EAAkB;AACvE,EAAA,OAAO,EAAE,KAAA,EAAO,CAAA,EAAG,MAAA,EAAQ,QAAQ,8CAAA,EAAiB;AACtD;AAEA,SAAS,eAAA,CAAgB,OAAwB,MAAA,EAA6D;AAC5G,EAAA,IAAI,KAAA,CAAM,0BAA0B,IAAA,EAAM;AACxC,IAAA,OAAO,EAAE,KAAA,EAAO,EAAA,EAAI,MAAA,EAAQ,QAAQ,gCAAA,EAAQ;AAAA,EAC9C;AACA,EAAA,IAAI,KAAA,CAAM,0BAA0B,KAAA,EAAO;AACzC,IAAA,OAAO,EAAE,KAAA,EAAO,EAAA,EAAI,MAAA,EAAQ,QAAQ,gCAAA,EAAQ;AAAA,EAC9C;AACA,EAAA,OAAO,EAAE,KAAA,EAAO,EAAA,EAAI,MAAA,EAAQ,QAAQ,sCAAA,EAAS;AAC/C;AAKO,SAAS,UAAA,CAAW,MAAA,EAA2B,OAAA,GAAU,eAAA,EAAoC;AAClG,EAAA,OAAO,OAAO,GAAA,CAAI,CAAC,MAAM,eAAA,CAAgB,CAAA,EAAG,OAAO,CAAC,CAAA;AACtD;;;ACxKO,SAAS,gBAAA,CACd,MAAA,EACA,OAAA,GAA8B,EAAC,EAChB;AACf,EAAA,MAAM,IAAA,GAAO,QAAQ,QAAA,IAAY,GAAA;AACjC,EAAA,MAAM,GAAA,GAAM,QAAQ,WAAA,IAAe,SAAA;AAGnC,EAAA,IAAI,MAAA,CAAO,MAAA,KAAW,CAAA,EAAG,OAAO,EAAC;AAEjC,EAAA,MAAM,MAAA,GAAS,MAAA,CAAO,CAAC,CAAA,CAAE,GAAA;AACzB,EAAA,MAAM,MAAA,GAAS,MAAA,CAAO,CAAC,CAAA,CAAE,GAAA;AACzB,EAAA,MAAM,UAAA,GAAa,MAAA;AACnB,EAAA,MAAM,aAAa,MAAA,GAAU,IAAA,CAAK,IAAK,MAAA,GAAS,IAAA,CAAK,KAAM,GAAG,CAAA;AAE9D,EAAA,MAAM,IAAA,uBAAW,GAAA,EAAkG;AACnH,EAAA,KAAA,MAAW,KAAK,MAAA,EAAQ;AACtB,IAAA,MAAM,CAAA,GAAA,CAAK,CAAA,CAAE,GAAA,GAAM,MAAA,IAAU,UAAA;AAC7B,IAAA,MAAM,CAAA,GAAA,CAAK,CAAA,CAAE,GAAA,GAAM,MAAA,IAAU,UAAA;AAC7B,IAAA,MAAM,EAAA,GAAK,IAAA,CAAK,KAAA,CAAM,CAAA,GAAI,IAAI,CAAA;AAC9B,IAAA,MAAM,EAAA,GAAK,IAAA,CAAK,KAAA,CAAM,CAAA,GAAI,IAAI,CAAA;AAC9B,IAAA,MAAM,GAAA,GAAM,CAAA,EAAG,EAAE,CAAA,CAAA,EAAI,EAAE,CAAA,CAAA;AACvB,IAAA,MAAM,GAAA,GAAM,IAAA,CAAK,GAAA,CAAI,GAAG,CAAA;AACxB,IAAA,IAAI,CAAC,GAAA,EAAK;AACR,MAAA,IAAA,CAAK,IAAI,GAAA,EAAK;AAAA,QACZ,KAAK,CAAA,CAAE,WAAA;AAAA,QACP,KAAA,EAAO,CAAA;AAAA,QACP,MAAM,CAAA,CAAE,WAAA;AAAA,QACR,MAAM,CAAA,CAAE,WAAA;AAAA,QACR,KAAK,CAAA,CAAE,GAAA;AAAA,QACP,KAAK,CAAA,CAAE;AAAA,OACR,CAAA;AAAA,IACH,CAAA,MAAO;AACL,MAAA,GAAA,CAAI,OAAO,CAAA,CAAE,WAAA;AACb,MAAA,GAAA,CAAI,KAAA,IAAS,CAAA;AACb,MAAA,IAAI,EAAE,WAAA,GAAc,GAAA,CAAI,IAAA,EAAM,GAAA,CAAI,OAAO,CAAA,CAAE,WAAA;AAC3C,MAAA,IAAI,EAAE,WAAA,GAAc,GAAA,CAAI,IAAA,EAAM,GAAA,CAAI,OAAO,CAAA,CAAE,WAAA;AAC3C,MAAA,GAAA,CAAI,GAAA,GAAA,CAAO,IAAI,GAAA,IAAO,GAAA,CAAI,QAAQ,CAAA,CAAA,GAAK,CAAA,CAAE,OAAO,GAAA,CAAI,KAAA;AACpD,MAAA,GAAA,CAAI,GAAA,GAAA,CAAO,IAAI,GAAA,IAAO,GAAA,CAAI,QAAQ,CAAA,CAAA,GAAK,CAAA,CAAE,OAAO,GAAA,CAAI,KAAA;AAAA,IACtD;AAAA,EACF;AAEA,EAAA,MAAM,QAAuB,EAAC;AAC9B,EAAA,KAAA,MAAW,CAAA,IAAK,IAAA,CAAK,MAAA,EAAO,EAAG;AAC7B,IAAA,IAAI,KAAA;AACJ,IAAA,IAAI,GAAA,KAAQ,KAAA,EAAO,KAAA,GAAQ,CAAA,CAAE,IAAA;AAAA,SAAA,IACpB,GAAA,KAAQ,KAAA,EAAO,KAAA,GAAQ,CAAA,CAAE,IAAA;AAAA,SAC7B,KAAA,GAAQ,CAAA,CAAE,GAAA,GAAM,CAAA,CAAE,KAAA;AACvB,IAAA,KAAA,CAAM,IAAA,CAAK;AAAA,MACT,KAAK,CAAA,CAAE,GAAA;AAAA,MACP,KAAK,CAAA,CAAE,GAAA;AAAA,MACP,WAAA,EAAa,IAAA,CAAK,KAAA,CAAM,KAAK,CAAA;AAAA,MAC7B,WAAW,CAAA,CAAE;AAAA,KACd,CAAA;AAAA,EACH;AACA,EAAA,OAAO,KAAA;AACT;AAEO,SAAS,iBAAiB,KAAA,EAAgE;AAC/F,EAAA,OAAO;AAAA,IACL,IAAA,EAAM,mBAAA;AAAA,IACN,QAAA,EAAU,KAAA,CAAM,GAAA,CAAI,CAAC,CAAA,MAAO;AAAA,MAC1B,IAAA,EAAM,SAAA;AAAA,MACN,QAAA,EAAU,EAAE,IAAA,EAAM,OAAA,EAAS,WAAA,EAAa,CAAC,CAAA,CAAE,GAAA,EAAK,CAAA,CAAE,GAAG,CAAA,EAAE;AAAA,MACvD,UAAA,EAAY;AAAA,QACV,aAAa,CAAA,CAAE,WAAA;AAAA,QACf,WAAW,CAAA,CAAE;AAAA;AACf,KACF,CAAE;AAAA,GACJ;AACF;AAGO,SAAS,qBAAA,CACd,KAAA,EACA,IAAA,GAAO,EAAA,EACP;AACA,EAAA,OAAO,CAAC,GAAG,KAAK,CAAA,CAAE,KAAK,CAAC,CAAA,EAAG,CAAA,KAAM,CAAA,CAAE,cAAc,CAAA,CAAE,WAAW,CAAA,CAAE,KAAA,CAAM,GAAG,IAAI,CAAA;AAC/E;;;AC7EO,IAAM,iBAAN,MAAqB;AAAA,EAClB,GAAA;AAAA,EACA,OAAA;AAAA,EACA,QAAA;AAAA,EACA,WAAA;AAAA,EACA,SAAA,uBAAgB,GAAA,EAAc;AAAA,EAC9B,UAAA,GAAkC,IAAA;AAAA,EAClC,WAAqB,EAAC;AAAA,EAE9B,YAAY,OAAA,EAAgC;AAC1C,IAAA,IAAA,CAAK,MAAM,OAAA,CAAQ,GAAA;AACnB,IAAA,IAAA,CAAK,UAAU,OAAA,CAAQ,OAAA;AACvB,IAAA,IAAA,CAAK,QAAA,GAAW,QAAQ,QAAA,IAAY,GAAA;AACpC,IAAA,IAAA,CAAK,WAAA,GAAc,QAAQ,WAAA,IAAe,WAAA;AAAA,EAC5C;AAAA;AAAA,EAGA,QAAA,CAAS,UAAU,eAAA,EAA+B;AAChD,IAAA,MAAM,EAAA,GAAM,OAAO,WAAA,KAAgB,WAAA,GAAc,YAAY,GAAA,EAAI,GAAI,KAAK,GAAA,EAAI;AAC9E,IAAA,MAAM,QAAA,GAAW,IAAI,GAAA,CAAI,IAAA,CAAK,QAAQ,KAAA,CAAM,GAAA,CAAI,CAAC,CAAA,KAAM,CAAC,CAAA,CAAE,EAAA,EAAI,CAAC,CAAU,CAAC,CAAA;AAE1E,IAAA,MAAM,SAAS,IAAA,CAAK,OAAA,CAAQ,KAAA,CAAM,GAAA,CAAI,CAAC,CAAA,KAAM;AAC3C,MAAA,MAAM,KAAA,GAAyB;AAAA,QAC7B,WAAA,EAAa,EAAE,UAAA,EAAY,WAAA;AAAA,QAC3B,QAAA,EAAU,EAAE,UAAA,EAAY,QAAA;AAAA,QACxB,YAAA,EAAc,CAAA,CAAE,UAAA,EAAY,YAAA,IAAgB,CAAA;AAAA,QAC5C,QAAA,EAAU,EAAE,UAAA,EAAY,QAAA;AAAA,QACxB,aAAA,EAAe,EAAE,UAAA,EAAY,aAAA;AAAA,QAC7B,qBAAA,EAAuB,CAAA,CAAE,UAAA,EAAY,qBAAA,IAAyB,KAAA;AAAA,QAC9D,MAAA,EAAQ,EAAE,UAAA,EAAY;AAAA,OACxB;AACA,MAAA,OAAO,EAAE,QAAQ,CAAA,CAAE,EAAA,EAAI,OAAO,eAAA,CAAgB,KAAA,EAAO,OAAO,CAAA,EAAE;AAAA,IAChE,CAAC,CAAA;AAED,IAAA,MAAM,MAAA,GAAS,MAAA,CAAO,OAAA,CAAQ,CAAC,GAAG,CAAA,KAAM;AACtC,MAAA,MAAM,IAAA,GAAO,IAAA,CAAK,OAAA,CAAQ,KAAA,CAAM,CAAC,CAAA;AACjC,MAAA,MAAM,IAAA,GAAO,QAAA,CAAS,GAAA,CAAI,IAAA,CAAK,QAAQ,CAAA;AACvC,MAAA,MAAM,EAAA,GAAK,QAAA,CAAS,GAAA,CAAI,IAAA,CAAK,MAAM,CAAA;AACnC,MAAA,IAAI,CAAC,IAAA,IAAQ,CAAC,EAAA,SAAW,EAAC;AAC1B,MAAA,OAAO;AAAA,QACL;AAAA,UACE,GAAA,EAAA,CAAM,IAAA,CAAK,GAAA,GAAM,EAAA,CAAG,GAAA,IAAO,CAAA;AAAA,UAC3B,GAAA,EAAA,CAAM,IAAA,CAAK,GAAA,GAAM,EAAA,CAAG,GAAA,IAAO,CAAA;AAAA,UAC3B,WAAA,EAAa,EAAE,KAAA,CAAM;AAAA;AACvB,OACF;AAAA,IACF,CAAC,CAAA;AAED,IAAA,MAAM,UAAU,gBAAA,CAAiB,MAAA,EAAQ,EAAE,QAAA,EAAU,IAAA,CAAK,UAAU,CAAA;AACpE,IAAA,MAAM,WAAA,GAAc,qBAAA;AAAA,MAClB,MAAA,CAAO,OAAA,CAAQ,CAAC,CAAA,EAAG,CAAA,KAAM;AACvB,QAAA,MAAM,IAAA,GAAO,IAAA,CAAK,OAAA,CAAQ,KAAA,CAAM,CAAC,CAAA;AACjC,QAAA,MAAM,IAAA,GAAO,QAAA,CAAS,GAAA,CAAI,IAAA,CAAK,QAAQ,CAAA;AACvC,QAAA,MAAM,EAAA,GAAK,QAAA,CAAS,GAAA,CAAI,IAAA,CAAK,MAAM,CAAA;AACnC,QAAA,IAAI,CAAC,IAAA,IAAQ,CAAC,EAAA,SAAW,EAAC;AAC1B,QAAA,OAAO;AAAA,UACL;AAAA,YACE,IAAI,IAAA,CAAK,EAAA;AAAA,YACT,WAAA,EAAa,EAAE,KAAA,CAAM,KAAA;AAAA,YACrB,GAAA,EAAA,CAAM,IAAA,CAAK,GAAA,GAAM,EAAA,CAAG,GAAA,IAAO,CAAA;AAAA,YAC3B,GAAA,EAAA,CAAM,IAAA,CAAK,GAAA,GAAM,EAAA,CAAG,GAAA,IAAO,CAAA;AAAA,YAC3B,OAAO,IAAA,CAAK;AAAA;AACd,SACF;AAAA,MACF,CAAC;AAAA,KACH;AAEA,IAAA,MAAM,UAAA,GAAA,CACH,OAAO,WAAA,KAAgB,WAAA,GAAc,YAAY,GAAA,EAAI,GAAI,IAAA,CAAK,GAAA,EAAI,IAAK,EAAA;AAE1E,IAAA,MAAM,MAAA,GAAuB,EAAE,MAAA,EAAQ,OAAA,EAAS,aAAa,UAAA,EAAW;AACxE,IAAA,IAAA,CAAK,UAAA,GAAa,MAAA;AAClB,IAAA,IAAA,CAAK,cAAc,MAAM,CAAA;AACzB,IAAA,KAAA,MAAW,CAAA,IAAK,IAAA,CAAK,SAAA,EAAW,CAAA,CAAE,MAAM,CAAA;AACxC,IAAA,OAAO,MAAA;AAAA,EACT;AAAA;AAAA,EAGA,KAAA,GAAc;AACZ,IAAA,KAAA,MAAW,EAAA,IAAM,KAAK,QAAA,EAAU;AAC9B,MAAA,IAAA,CAAK,GAAA,CAAI,YAAY,EAAE,CAAA;AAAA,IACzB;AACA,IAAA,IAAA,CAAK,WAAW,EAAC;AAAA,EACnB;AAAA;AAAA,EAGA,OAAA,GAAgB;AACd,IAAA,IAAA,CAAK,KAAA,EAAM;AACX,IAAA,IAAA,CAAK,UAAU,KAAA,EAAM;AACrB,IAAA,IAAA,CAAK,UAAA,GAAa,IAAA;AAAA,EACpB;AAAA;AAAA,EAGA,SAAS,EAAA,EAA0B;AACjC,IAAA,IAAA,CAAK,SAAA,CAAU,IAAI,EAAE,CAAA;AACrB,IAAA,OAAO,MAAM,IAAA,CAAK,SAAA,CAAU,MAAA,CAAO,EAAE,CAAA;AAAA,EACvC;AAAA;AAAA,EAGA,aAAA,GAAqC;AACnC,IAAA,OAAO,IAAA,CAAK,UAAA;AAAA,EACd;AAAA,EAEQ,cAAc,MAAA,EAA4B;AAChD,IAAA,IAAA,CAAK,KAAA,EAAM;AACX,IAAA,MAAM,KAAA,GAAS,KAAK,GAAA,CAMjB,QAAA;AACH,IAAA,MAAM,IAAA,GAAO,gBAAA,CAAiB,MAAA,CAAO,OAAO,CAAA;AAC5C,IAAA,MAAM,QAAA,GAAW,CAAA,EAAG,IAAA,CAAK,WAAW,CAAA,SAAA,CAAA;AACpC,IAAA,MAAM,OAAA,GAAU,CAAA,EAAG,IAAA,CAAK,WAAW,CAAA,WAAA,CAAA;AACnC,IAAA,IAAI,CAAC,MAAM,SAAA,CAAU,QAAQ,GAAG,KAAA,CAAM,SAAA,CAAU,UAAU,IAAI,CAAA;AAC9D,IAAA,IAAI;AACF,MAAA,KAAA,CAAM,QAAA,CAAS;AAAA,QACb,EAAA,EAAI,OAAA;AAAA,QACJ,IAAA,EAAM,SAAA;AAAA,QACN,MAAA,EAAQ,QAAA;AAAA,QACR,KAAA,EAAO;AAAA,UACL,gBAAA,EAAkB;AAAA,YAChB,aAAA;AAAA,YACA,CAAC,QAAQ,CAAA;AAAA,YACT,CAAC,OAAO,aAAa,CAAA;AAAA,YACrB,CAAA;AAAA,YAAG,CAAA;AAAA,YACH,EAAA;AAAA,YAAI,GAAA;AAAA,YACJ,GAAA;AAAA,YAAK;AAAA,WACP;AAAA,UACA,mBAAA,EAAqB,CAAA;AAAA,UACrB,eAAA,EAAiB;AAAA,YACf,aAAA;AAAA,YACA,CAAC,QAAQ,CAAA;AAAA,YACT,CAAC,iBAAiB,CAAA;AAAA,YAClB,CAAA;AAAA,YAAG,mBAAA;AAAA,YACH,GAAA;AAAA,YAAK,qBAAA;AAAA,YACL,GAAA;AAAA,YAAK,sBAAA;AAAA,YACL,GAAA;AAAA,YAAK,qBAAA;AAAA,YACL,CAAA;AAAA,YAAG;AAAA,WACL;AAAA,UACA,gBAAA,EAAkB,EAAA;AAAA,UAClB,iBAAA,EAAmB;AAAA;AACrB,OACD,CAAA;AACD,MAAA,IAAA,CAAK,QAAA,CAAS,KAAK,OAAO,CAAA;AAAA,IAC5B,CAAA,CAAA,MAAQ;AAAA,IAER;AAAA,EACF;AACF","file":"chunk-3656CGXE.js","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: p.properties?.failureCount ?? 0,\n pressure: p.properties?.pressure,\n ratedPressure: p.properties?.ratedPressure,\n hasCathodicProtection: p.properties?.hasCathodicProtection ?? false,\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"]}
@@ -1,259 +0,0 @@
1
- import { paintPipeBy } from './chunk-QVY4WJLM.js';
2
- import { upsertSource } from '@caoguo/maplibre';
3
-
4
- var PipelineTopology = class {
5
- map;
6
- dataset;
7
- colorBy;
8
- layerPrefix;
9
- pipelineTypes;
10
- layerIds = [];
11
- nodeListeners = /* @__PURE__ */ new Set();
12
- pipeListeners = /* @__PURE__ */ new Set();
13
- drillListeners = /* @__PURE__ */ new Set();
14
- /** 当前钻取区域(null = 全量) */
15
- currentRegion = null;
16
- /** 当前分层过滤器 */
17
- layerFilter = null;
18
- /** click 事件是否已绑定 */
19
- clickBound = false;
20
- constructor(options) {
21
- this.map = options.map;
22
- this.dataset = options.dataset;
23
- this.colorBy = options.colorBy ?? "type";
24
- this.pipelineTypes = options.pipelineTypes;
25
- this.layerPrefix = options.layerPrefix ?? "cg-topo";
26
- }
27
- /** 渲染管段+节点到地图 */
28
- render() {
29
- this.clear();
30
- const mlMap = this.map.instance;
31
- const prefix = this.layerPrefix;
32
- const pipeGeoJSON = {
33
- type: "FeatureCollection",
34
- features: this.dataset.pipes.filter((p) => this.isPipeVisible(p)).flatMap((p) => {
35
- const from = this.dataset.nodes.find((n) => n.id === p.fromNode);
36
- const to = this.dataset.nodes.find((n) => n.id === p.toNode);
37
- if (!from || !to) return [];
38
- const coords = p.geometry && p.geometry.length >= 2 ? p.geometry : [
39
- [from.lng, from.lat],
40
- [to.lng, to.lat]
41
- ];
42
- return [
43
- {
44
- type: "Feature",
45
- geometry: { type: "LineString", coordinates: coords },
46
- properties: {
47
- pipeId: p.id,
48
- pipelineType: p.pipelineType ?? "",
49
- diameter: p.properties?.diameter ?? 0,
50
- status: p.properties?.status ?? "unknown",
51
- material: p.properties?.material ?? "unknown"
52
- }
53
- }
54
- ];
55
- })
56
- };
57
- const nodeGeoJSON = {
58
- type: "FeatureCollection",
59
- features: this.dataset.nodes.filter((n) => this.isNodeVisible(n)).map((n) => ({
60
- type: "Feature",
61
- geometry: { type: "Point", coordinates: [n.lng, n.lat] },
62
- properties: { nodeId: n.id, kind: n.kind, pipelineType: n.pipelineType ?? "" }
63
- }))
64
- };
65
- upsertSource(mlMap, `${prefix}-pipes-src`, pipeGeoJSON);
66
- upsertSource(mlMap, `${prefix}-nodes-src`, nodeGeoJSON);
67
- mlMap.addLayer({
68
- id: `${prefix}-pipes-line`,
69
- type: "line",
70
- source: `${prefix}-pipes-src`,
71
- paint: {
72
- "line-color": paintPipeBy(this.colorBy, { types: this.pipelineTypes }),
73
- "line-width": 2,
74
- "line-opacity": 0.85
75
- }
76
- });
77
- mlMap.addLayer({
78
- id: `${prefix}-nodes-pt`,
79
- type: "circle",
80
- source: `${prefix}-nodes-src`,
81
- paint: {
82
- "circle-radius": 4,
83
- "circle-color": "#f59e0b",
84
- "circle-stroke-width": 1.5,
85
- "circle-stroke-color": "#ffffff"
86
- }
87
- });
88
- this.layerIds.push(`${prefix}-pipes-line`, `${prefix}-nodes-pt`);
89
- this.bindClickEvents(mlMap, prefix);
90
- }
91
- /**
92
- * 绑定点击事件(设备卡片:点击节点/管段触发订阅回调)。
93
- * 只绑定一次(render 重建图层后事件仍指向固定 layerId)。
94
- */
95
- bindClickEvents(mlMap, prefix) {
96
- if (this.clickBound) return;
97
- this.clickBound = true;
98
- const nodeById = new Map(this.dataset.nodes.map((n) => [n.id, n]));
99
- const pipeById = new Map(this.dataset.pipes.map((p) => [p.id, p]));
100
- mlMap.on("click", `${prefix}-pipes-line`, (e) => {
101
- const features = e.features ?? [];
102
- const f = features[0];
103
- const pipe = f?.properties?.pipeId ? pipeById.get(f.properties.pipeId) : void 0;
104
- if (pipe) {
105
- for (const l of this.pipeListeners) l({ pipe });
106
- }
107
- });
108
- mlMap.on("click", `${prefix}-nodes-pt`, (e) => {
109
- const features = e.features ?? [];
110
- const f = features[0];
111
- const node = f?.properties?.nodeId ? nodeById.get(f.properties.nodeId) : void 0;
112
- if (node) {
113
- for (const l of this.nodeListeners) l({ node });
114
- }
115
- });
116
- }
117
- /** 切换着色模式 */
118
- setColorBy(mode) {
119
- this.colorBy = mode;
120
- const mlMap = this.map.instance;
121
- if (mlMap.setPaintProperty) {
122
- try {
123
- mlMap.setPaintProperty(
124
- `${this.layerPrefix}-pipes-line`,
125
- "line-color",
126
- paintPipeBy(mode, { types: this.pipelineTypes })
127
- );
128
- } catch {
129
- }
130
- }
131
- }
132
- /** 清空所有图层 */
133
- clear() {
134
- for (const id of this.layerIds) {
135
- this.map.removeLayer(id);
136
- }
137
- this.layerIds = [];
138
- }
139
- /** 销毁组件 */
140
- destroy() {
141
- this.clear();
142
- this.nodeListeners.clear();
143
- this.pipeListeners.clear();
144
- this.drillListeners.clear();
145
- }
146
- /** 订阅节点点击事件 */
147
- onNodeSelect(fn) {
148
- this.nodeListeners.add(fn);
149
- return () => this.nodeListeners.delete(fn);
150
- }
151
- /** 订阅管段点击事件 */
152
- onPipeSelect(fn) {
153
- this.pipeListeners.add(fn);
154
- return () => this.pipeListeners.delete(fn);
155
- }
156
- /** 订阅钻取事件 */
157
- onDrillDown(fn) {
158
- this.drillListeners.add(fn);
159
- return () => this.drillListeners.delete(fn);
160
- }
161
- /** 管段是否可见(管线类型 + 区域钻取 + 分层过滤) */
162
- isPipeVisible(p) {
163
- if (this.pipelineTypes?.length && !this.pipelineTypes.includes(p.pipelineType)) {
164
- return false;
165
- }
166
- if (this.currentRegion && p.region !== this.currentRegion) return false;
167
- const f = this.layerFilter;
168
- if (f) {
169
- const props = p.properties ?? {};
170
- if (f.minDiameter !== void 0 && (props.diameter ?? 0) < f.minDiameter) return false;
171
- if (f.maxDiameter !== void 0 && (props.diameter ?? Infinity) > f.maxDiameter) return false;
172
- if (f.material && props.material !== f.material) return false;
173
- if (f.status && props.status !== f.status) return false;
174
- if (f.minAgeYears !== void 0 && props.installDate) {
175
- if (this.ageInYears(props.installDate) < f.minAgeYears) return false;
176
- }
177
- }
178
- return true;
179
- }
180
- /** 节点是否可见(管线类型 + 区域钻取) */
181
- isNodeVisible(n) {
182
- if (this.pipelineTypes?.length && !this.pipelineTypes.includes(n.pipelineType)) {
183
- return false;
184
- }
185
- if (this.currentRegion && n.region !== this.currentRegion) return false;
186
- return true;
187
- }
188
- /** 计算管段使用年限(年) */
189
- ageInYears(installDate) {
190
- const d = new Date(installDate).getTime();
191
- if (Number.isNaN(d)) return 0;
192
- return (Date.now() - d) / (365.25 * 24 * 3600 * 1e3);
193
- }
194
- /**
195
- * 搜索定位(按编号/地址/区域/类型)。
196
- * 返回匹配的节点与管段,供上层 flyTo 定位。
197
- */
198
- search(query) {
199
- const q = query.trim().toLowerCase();
200
- if (!q) return { nodes: [], pipes: [] };
201
- const nodes = this.dataset.nodes.filter(
202
- (n) => n.id.toLowerCase().includes(q) || (n.properties?.code ?? "").toLowerCase().includes(q) || (n.region ?? "").toLowerCase().includes(q) || n.kind.toLowerCase().includes(q)
203
- );
204
- const pipes = this.dataset.pipes.filter(
205
- (p) => p.id.toLowerCase().includes(q) || (p.region ?? "").toLowerCase().includes(q) || (p.properties?.material ?? "").toLowerCase().includes(q)
206
- );
207
- return { nodes, pipes };
208
- }
209
- /** 层级钻取:下钻到指定区域(过滤渲染 + 触发事件) */
210
- drillDown(region) {
211
- const from = this.currentRegion;
212
- this.currentRegion = region;
213
- this.render();
214
- for (const l of this.drillListeners) l({ from, to: region });
215
- }
216
- /** 层级钻取:返回上一级(清空区域过滤) */
217
- drillUp() {
218
- const from = this.currentRegion;
219
- this.currentRegion = null;
220
- this.render();
221
- for (const l of this.drillListeners) l({ from, to: "" });
222
- }
223
- /** 分层控制:按管径/材质/状态/年代过滤 */
224
- setLayerFilter(filter) {
225
- this.layerFilter = filter;
226
- this.render();
227
- }
228
- /** 清空分层过滤器 */
229
- clearLayerFilter() {
230
- this.layerFilter = null;
231
- this.render();
232
- }
233
- /** 高亮连通路径(基于节点 ID) */
234
- highlightConnectivity(centerId) {
235
- const adj = /* @__PURE__ */ new Map();
236
- for (const p of this.dataset.pipes) {
237
- if (!adj.has(p.fromNode)) adj.set(p.fromNode, /* @__PURE__ */ new Set());
238
- if (!adj.has(p.toNode)) adj.set(p.toNode, /* @__PURE__ */ new Set());
239
- adj.get(p.fromNode).add(p.toNode);
240
- adj.get(p.toNode).add(p.fromNode);
241
- }
242
- const visited = /* @__PURE__ */ new Set();
243
- const stack = [centerId];
244
- while (stack.length > 0) {
245
- const cur = stack.pop();
246
- if (visited.has(cur)) continue;
247
- visited.add(cur);
248
- const neighbors = adj.get(cur) ?? /* @__PURE__ */ new Set();
249
- for (const n of neighbors) {
250
- if (!visited.has(n)) stack.push(n);
251
- }
252
- }
253
- return visited;
254
- }
255
- };
256
-
257
- export { PipelineTopology };
258
- //# sourceMappingURL=chunk-BWQKY35F.js.map
259
- //# sourceMappingURL=chunk-BWQKY35F.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/topology/PipelineTopology.ts"],"names":[],"mappings":"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","file":"chunk-BWQKY35F.js","sourcesContent":["/**\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"]}