@caoguo/maplibre-pipeline 0.0.2 → 0.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/burst/index.cjs +708 -0
- package/dist/burst/index.cjs.map +1 -0
- package/dist/burst/index.js +4 -0
- package/dist/burst/index.js.map +1 -0
- package/dist/burstClass-CGMjcAYy.d.cts +161 -0
- package/dist/burstClass-Cf2p71LE.d.ts +161 -0
- package/dist/chunk-3656CGXE.js +340 -0
- package/dist/chunk-3656CGXE.js.map +1 -0
- package/dist/chunk-BWQKY35F.js +259 -0
- package/dist/chunk-BWQKY35F.js.map +1 -0
- package/dist/chunk-CDEYJ67B.js +80 -0
- package/dist/chunk-CDEYJ67B.js.map +1 -0
- package/dist/chunk-E4BRNDKY.js +13 -0
- package/dist/chunk-E4BRNDKY.js.map +1 -0
- package/dist/chunk-GCDA7KJM.js +289 -0
- package/dist/chunk-GCDA7KJM.js.map +1 -0
- package/dist/chunk-HCT6NSNS.js +532 -0
- package/dist/chunk-HCT6NSNS.js.map +1 -0
- package/dist/chunk-QVY4WJLM.js +256 -0
- package/dist/chunk-QVY4WJLM.js.map +1 -0
- package/dist/chunk-VSTG2ZUY.js +352 -0
- package/dist/chunk-VSTG2ZUY.js.map +1 -0
- package/dist/chunk-YBLESEN4.js +133 -0
- package/dist/chunk-YBLESEN4.js.map +1 -0
- package/dist/chunk-YS2ICFVK.js +101 -0
- package/dist/chunk-YS2ICFVK.js.map +1 -0
- package/dist/graph/index.cjs +382 -0
- package/dist/graph/index.cjs.map +1 -0
- package/dist/graph/index.js +4 -0
- package/dist/graph/index.js.map +1 -0
- package/dist/health/index.cjs +348 -0
- package/dist/health/index.cjs.map +1 -0
- package/dist/health/index.js +3 -0
- package/dist/health/index.js.map +1 -0
- package/dist/index.cjs +2389 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +10 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +12 -0
- package/dist/index.js.map +1 -0
- package/dist/leakage/index.cjs +7 -6
- package/dist/leakage/index.cjs.map +1 -1
- package/dist/leakage/index.d.cts +4 -3
- package/dist/leakage/index.d.ts +4 -3
- package/dist/leakage/index.js +1 -1
- package/dist/topology/index.cjs +113 -14
- package/dist/topology/index.cjs.map +1 -1
- package/dist/topology/index.d.cts +65 -15
- package/dist/topology/index.d.ts +65 -15
- package/dist/topology/index.js +1 -1
- package/dist/types/index.cjs.map +1 -1
- package/dist/types/index.d.cts +4 -0
- package/dist/types/index.d.ts +4 -0
- package/dist/types/index.js +1 -1
- package/package.json +6 -6
|
@@ -0,0 +1 @@
|
|
|
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"]}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
// src/graph/dfs.ts
|
|
2
|
+
function dfs(adj, start, opts = {}) {
|
|
3
|
+
const visited = [];
|
|
4
|
+
const parents = /* @__PURE__ */ new Map();
|
|
5
|
+
const visitedSet = /* @__PURE__ */ new Set();
|
|
6
|
+
let truncated = false;
|
|
7
|
+
if (!adj.has(start)) {
|
|
8
|
+
return { visited, parents, truncated };
|
|
9
|
+
}
|
|
10
|
+
if (opts.allowStart && !opts.allowStart(start)) {
|
|
11
|
+
return { visited, parents, truncated };
|
|
12
|
+
}
|
|
13
|
+
const stack = [{ id: start, depth: 0 }];
|
|
14
|
+
parents.set(start, null);
|
|
15
|
+
visitedSet.add(start);
|
|
16
|
+
while (stack.length > 0) {
|
|
17
|
+
const { id, depth } = stack.pop();
|
|
18
|
+
visited.push(id);
|
|
19
|
+
if (opts.maxDepth !== void 0 && depth >= opts.maxDepth) {
|
|
20
|
+
truncated = true;
|
|
21
|
+
continue;
|
|
22
|
+
}
|
|
23
|
+
const edges = adj.get(id) ?? [];
|
|
24
|
+
for (let i = edges.length - 1; i >= 0; i--) {
|
|
25
|
+
const e = edges[i];
|
|
26
|
+
if (opts.allowEdge && !opts.allowEdge(e, id, e.to)) continue;
|
|
27
|
+
if (visitedSet.has(e.to)) continue;
|
|
28
|
+
visitedSet.add(e.to);
|
|
29
|
+
parents.set(e.to, id);
|
|
30
|
+
stack.push({ id: e.to, depth: depth + 1 });
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return { visited, parents, truncated };
|
|
34
|
+
}
|
|
35
|
+
function detectCycles(adj) {
|
|
36
|
+
const cycles = [];
|
|
37
|
+
const visited = /* @__PURE__ */ new Set();
|
|
38
|
+
const onStack = /* @__PURE__ */ new Set();
|
|
39
|
+
const stack = [];
|
|
40
|
+
for (const node of adj.keys()) {
|
|
41
|
+
if (visited.has(node)) continue;
|
|
42
|
+
stack.push({
|
|
43
|
+
id: node,
|
|
44
|
+
iter: (adj.get(node) ?? [])[Symbol.iterator]()
|
|
45
|
+
});
|
|
46
|
+
onStack.add(node);
|
|
47
|
+
visited.add(node);
|
|
48
|
+
while (stack.length > 0) {
|
|
49
|
+
const top = stack[stack.length - 1];
|
|
50
|
+
const next = top.iter.next();
|
|
51
|
+
if (next.done) {
|
|
52
|
+
onStack.delete(top.id);
|
|
53
|
+
stack.pop();
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
const nextId = next.value.to;
|
|
57
|
+
if (onStack.has(nextId)) {
|
|
58
|
+
const cycle = [nextId];
|
|
59
|
+
for (let i = stack.length - 1; i >= 0; i--) {
|
|
60
|
+
cycle.push(stack[i].id);
|
|
61
|
+
if (stack[i].id === nextId) break;
|
|
62
|
+
}
|
|
63
|
+
cycle.reverse();
|
|
64
|
+
if (new Set(cycle).size >= 3) cycles.push(cycle);
|
|
65
|
+
} else if (!visited.has(nextId)) {
|
|
66
|
+
visited.add(nextId);
|
|
67
|
+
onStack.add(nextId);
|
|
68
|
+
stack.push({
|
|
69
|
+
id: nextId,
|
|
70
|
+
iter: (adj.get(nextId) ?? [])[Symbol.iterator]()
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return cycles;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export { detectCycles, dfs };
|
|
79
|
+
//# sourceMappingURL=chunk-CDEYJ67B.js.map
|
|
80
|
+
//# sourceMappingURL=chunk-CDEYJ67B.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/graph/dfs.ts"],"names":[],"mappings":"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","file":"chunk-CDEYJ67B.js","sourcesContent":["/**\n * DFS(深度优先遍历)\n *\n * 用途:\n * - 管线编辑后构建新的连通分量\n * - 环检测(爆管推演风险评估)\n * - 上游路径追踪\n */\n\nimport type { AdjacencyList, AdjEdge } from './adjacency';\n\nexport interface DfsOptions {\n /** 最大深度,默认无限制 */\n maxDepth?: number;\n /** 邻居过滤(在遍历孩子前判断) */\n allowEdge?: (edge: AdjEdge, from: string, to: string) => boolean;\n /** 起点过滤器 */\n allowStart?: (node: string) => boolean;\n}\n\nexport interface DfsResult {\n visited: string[];\n parents: Map<string, string | null>;\n /** 是否触发了 maxDepth 截断(true=未完全遍历) */\n truncated: boolean;\n}\n\n/**\n * 深度优先遍历\n */\nexport function dfs(\n adj: AdjacencyList,\n start: string,\n opts: DfsOptions = {}\n): DfsResult {\n const visited: string[] = [];\n const parents = new Map<string, string | null>();\n const visitedSet = new Set<string>();\n let truncated = false;\n\n if (!adj.has(start)) {\n return { visited, parents, truncated };\n }\n if (opts.allowStart && !opts.allowStart(start)) {\n return { visited, parents, truncated };\n }\n\n const stack: Array<{ id: string; depth: number }> = [{ id: start, depth: 0 }];\n parents.set(start, null);\n visitedSet.add(start);\n\n while (stack.length > 0) {\n const { id, depth } = stack.pop()!;\n visited.push(id);\n\n if (opts.maxDepth !== undefined && depth >= opts.maxDepth) {\n truncated = true;\n continue;\n }\n\n const edges = adj.get(id) ?? [];\n // 倒序遍历以保持\"原本右子树先访问\"的 DFS 直观\n for (let i = edges.length - 1; i >= 0; i--) {\n const e = edges[i];\n if (opts.allowEdge && !opts.allowEdge(e, id, e.to)) continue;\n if (visitedSet.has(e.to)) continue;\n visitedSet.add(e.to);\n parents.set(e.to, id);\n stack.push({ id: e.to, depth: depth + 1 });\n }\n }\n\n return { visited, parents, truncated };\n}\n\n/**\n * 检测有向图中的所有环(基于 Tarjan 算法的简化版本)。\n * 返回包含环的所有节点集合(连通分量内存在环时整分量被标记)。\n */\nexport function detectCycles(adj: AdjacencyList): string[][] {\n const cycles: string[][] = [];\n const visited = new Set<string>();\n const onStack = new Set<string>();\n const stack: Array<{ id: string; iter: Iterator<AdjEdge> }> = [];\n\n for (const node of adj.keys()) {\n if (visited.has(node)) continue;\n const localCycle: string[] = [];\n stack.push({\n id: node,\n iter: (adj.get(node) ?? [])[Symbol.iterator](),\n });\n onStack.add(node);\n visited.add(node);\n\n while (stack.length > 0) {\n const top = stack[stack.length - 1];\n const next = top.iter.next();\n if (next.done) {\n onStack.delete(top.id);\n stack.pop();\n continue;\n }\n const nextId = next.value.to;\n if (onStack.has(nextId)) {\n // 找到环:提取 onStack 中从 nextId 到 top.id 的所有节点\n const cycle: string[] = [nextId];\n for (let i = stack.length - 1; i >= 0; i--) {\n cycle.push(stack[i].id);\n if (stack[i].id === nextId) break;\n }\n cycle.reverse();\n // 双向建表会把 s->v1->s 视为 2 节点\"环\"(实质是同一物理边的双向表示),\n // 只保留 ≥3 个不同节点的\"真环\"\n if (new Set(cycle).size >= 3) cycles.push(cycle);\n } else if (!visited.has(nextId)) {\n visited.add(nextId);\n onStack.add(nextId);\n stack.push({\n id: nextId,\n iter: (adj.get(nextId) ?? [])[Symbol.iterator](),\n });\n }\n }\n }\n\n return cycles;\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/types/index.ts"],"names":[],"mappings":";AA0BO,IAAM,cAAA,GAA0C;AAAA,EACrD,KAAA;AAAA,EACA,OAAA;AAAA,EACA,UAAA;AAAA,EACA,SAAA;AAAA,EACA,OAAA;AAAA,EACA;AACF","file":"chunk-E4BRNDKY.js","sourcesContent":["/**\n * 草果地图管网组件包 - 类型定义\n *\n * 地下管网 MVP 数据模型:\n * - 节点(Node):管段的交汇点/设备点(阀门/泵/表/源头)\n * - 管段(Pipe/Edge):连接两个节点的物理管线\n * - 用户(User):连接到节点的消费方(居民/商业/工业)\n *\n * 拓扑数据结构参见 PRD phase-1-pipeline §4.1.2\n */\n\n// ============================================================\n// 一、管线类型(基础设施大类)\n// ============================================================\n/**\n * 管网大类\n * - gas:燃气\n * - water:供水\n * - drainage:排水\n * - heating:供热\n * - power:电力(管沟)\n * - telecom:通信(管沟)\n */\nexport type PipelineType = 'gas' | 'water' | 'drainage' | 'heating' | 'power' | 'telecom';\n\n/** 所有支持的管线大类(便于运行时校验) */\nexport const PIPELINE_TYPES: readonly PipelineType[] = [\n 'gas',\n 'water',\n 'drainage',\n 'heating',\n 'power',\n 'telecom',\n] as const;\n\n// ============================================================\n// 二、节点(Node)\n// ============================================================\n/**\n * 节点类型\n * - junction:普通连接点(三通/四通/弯头等)\n * - valve:阀门(可关闭以隔离故障段)\n * - pump:泵站(供水加压/供热循环)\n * - meter:计量表/用户表\n * - source:源头(制水厂/燃气门站/热源厂)\n * - tank:储水罐/储气罐\n * - junction_box:电缆/光缆接线井\n */\nexport type NodeKind =\n | 'junction'\n | 'valve'\n | 'pump'\n | 'meter'\n | 'source'\n | 'tank'\n | 'junction_box';\n\n/** 阀门子类型 */\nexport type ValveType = 'gate' | 'butterfly' | 'check' | 'pressure_reduction' | 'control';\n\n/** 阀门状态(用于爆管推演隔离方案) */\nexport type ValveStatus = 'open' | 'closed' | 'partial';\n\n// ============================================================\n// 三、管段(Pipe/Edge)\n// ============================================================\n/**\n * 管段材质\n * - cast_iron:铸铁(旧管网主力)\n * - ductile_iron:球墨铸铁(当前主流)\n * - steel:钢管(高压/燃气主干)\n * - pe:聚乙烯(PE,现代供水/燃气)\n * - pvc:聚氯乙烯(PVC,排水)\n * - concrete:混凝土(排水/给水主干)\n * - hdpe:高密度聚乙烯\n * - copper:铜管(室内引入管)\n * - unknown:未知\n */\nexport type PipeMaterial =\n | 'cast_iron'\n | 'ductile_iron'\n | 'steel'\n | 'pe'\n | 'pvc'\n | 'concrete'\n | 'hdpe'\n | 'copper'\n | 'unknown';\n\n/**\n * 管段状态\n * - normal:正常运行\n * - aging:老化(建议关注)\n * - damaged:损坏(泄漏/破裂)\n * - under_repair:维修中(部分阀门/路段暂停使用)\n * - abandoned:废弃\n * - unknown:未知\n */\nexport type PipeStatus =\n | 'normal'\n | 'aging'\n | 'damaged'\n | 'under_repair'\n | 'abandoned'\n | 'unknown';\n\n// ============================================================\n// 四、设备附加属性(管段 / 节点公用扩展)\n// ============================================================\nexport interface PipeProperties {\n /** 公称直径(mm),如 DN150 = 150 */\n diameter?: number;\n /** 管材 */\n material?: PipeMaterial;\n /** 安装日期(ISO 字符串) */\n installDate?: string;\n /** 工作压力(MPa) */\n pressure?: number;\n /** 最高允许压力(MPa) */\n ratedPressure?: number;\n /** 历史故障次数(健康评分 history 维,PRD §4.4.2) */\n failureCount?: number;\n /** 是否阴极保护(健康评分 protection 维,PRD §4.4.2) */\n hasCathodicProtection?: boolean;\n /** 当前状态 */\n status?: PipeStatus;\n /** 埋深(m) */\n depth?: number;\n /** 建设单位 */\n owner?: string;\n /** 自由扩展(运维记录/检测报告 URL 等) */\n extra?: Record<string, unknown>;\n}\n\nexport interface NodeProperties {\n /** 设备编号 */\n code?: string;\n /** 阀门类型(仅 valve) */\n valveType?: ValveType;\n /** 阀门状态(仅 valve) */\n valveStatus?: ValveStatus;\n /** 流量/供气能力(m³/h,仅 source/pump) */\n capacity?: number;\n /** 安装日期 */\n installDate?: string;\n /** 产权单位 */\n owner?: string;\n /** 海拔(m,用于地形修正) */\n elevation?: number;\n /** 自由扩展 */\n extra?: Record<string, unknown>;\n}\n\n// ============================================================\n// 五、节点(Node)\n// ============================================================\n/**\n * 管网节点\n * position 为 [lng, lat](GeoJSON 习惯),不是 [lat, lng]\n */\nexport interface PipelineNode {\n id: string;\n /** 节点类型 */\n kind: NodeKind;\n /** 经度(WGS84) */\n lng: number;\n /** 纬度(WGS84) */\n lat: number;\n /** 所属管线大类(燃气/供水等) */\n pipelineType?: PipelineType;\n /** 行政区/区域(用于层级钻取) */\n region?: string;\n /** 自定义属性 */\n properties?: NodeProperties;\n}\n\n// ============================================================\n// 六、管段(Pipe/Edge)\n// ============================================================\n/**\n * 管网管段\n * from_node / to_node 是有向连接,但物理上双向导通。\n * 地理 LineString 用 [[lng,lat], ...] 表示。\n */\nexport interface PipelinePipe {\n id: string;\n /** 起点节点 id */\n fromNode: string;\n /** 终点节点 id */\n toNode: string;\n /** 管段类型,固定为 'pipe'(预留扩展如 'channel'/'duct') */\n type: 'pipe';\n /** 所属管线大类 */\n pipelineType?: PipelineType;\n /** 区域编码(用于 LOD 按需加载) */\n region?: string;\n /** 几何(可选,未提供时按 from/to 拉直线) */\n geometry?: [number, number][];\n /** 长度(m),可选,由 geometry 计算 */\n length?: number;\n /** 管段属性 */\n properties?: PipeProperties;\n}\n\n// ============================================================\n// 七、用户/建筑(爆管推演影响分析用)\n// ============================================================\n/**\n * 用户类型\n * - residential:居民\n * - commercial:商业\n * - industrial:工业\n * - important:重要用户(医院/学校/政府部门/消防)\n */\nexport type UserKind = 'residential' | 'commercial' | 'industrial' | 'important';\n\nexport interface PipelineUser {\n id: string;\n /** 用户名 */\n name?: string;\n /** 类型 */\n kind: UserKind;\n /** 所属节点 id(直接或间接连接) */\n nodeId?: string;\n /** 位置 */\n lng: number;\n lat: number;\n /** 区域 */\n region?: string;\n /** 人口/用量(可选) */\n scale?: number;\n /** 备注 */\n note?: string;\n}\n\n// ============================================================\n// 八、完整管网拓扑\n// ============================================================\n/**\n * 管网拓扑数据集(完整 MVP 输入)\n * - nodes:节点列表\n * - pipes:管段列表\n * - users:用户列表(可选,爆管推演需要)\n */\nexport interface PipelineTopologyDataset {\n nodes: PipelineNode[];\n pipes: PipelinePipe[];\n users?: PipelineUser[];\n}\n\n// ============================================================\n// 九、事件载荷(包内统一事件类型)\n// ============================================================\n/** 选中节点 */\nexport interface NodeSelectEvent {\n node: PipelineNode;\n /** 在鼠标事件中的屏幕坐标(可选) */\n point?: { x: number; y: number };\n}\n\n/** 选中管段 */\nexport interface PipeSelectEvent {\n pipe: PipelinePipe;\n point?: { x: number; y: number };\n}\n\n/** 钻取事件(区域层级下钻) */\nexport interface DrillDownEvent {\n /** 上一级区域 */\n from: string | null;\n /** 目标区域 */\n to: string;\n}\n\n// ============================================================\n// 十、可视化着色规则(caoguo-pipeline 主题)\n// ============================================================\n/**\n * 着色模式(管线渲染)\n * - type:按管线大类着色(燃气黄/供水蓝等)\n * - diameter:按管径着色(PRD §4.1.4)\n * - status:按管段状态着色\n * - material:按管材着色\n * - health:按健康度着色(PipelineHealth 集成)\n * - uniform:单一颜色\n */\nexport type ColorByMode =\n | 'type'\n | 'diameter'\n | 'status'\n | 'material'\n | 'health'\n | 'uniform';\n"]}
|
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
// src/graph/adjacency.ts
|
|
2
|
+
function buildAdjacency(dataset) {
|
|
3
|
+
const adj = /* @__PURE__ */ new Map();
|
|
4
|
+
for (const node of dataset.nodes) {
|
|
5
|
+
if (!adj.has(node.id)) adj.set(node.id, []);
|
|
6
|
+
}
|
|
7
|
+
for (const pipe of dataset.pipes) {
|
|
8
|
+
const len = pipe.length ?? pipeLengthFromGeometry(pipe, dataset.nodes) ?? 0;
|
|
9
|
+
const fromList = adj.get(pipe.fromNode) ?? [];
|
|
10
|
+
fromList.push({
|
|
11
|
+
pipeId: pipe.id,
|
|
12
|
+
to: pipe.toNode,
|
|
13
|
+
length: len
|
|
14
|
+
});
|
|
15
|
+
adj.set(pipe.fromNode, fromList);
|
|
16
|
+
const toList = adj.get(pipe.toNode) ?? [];
|
|
17
|
+
toList.push({
|
|
18
|
+
pipeId: pipe.id,
|
|
19
|
+
to: pipe.fromNode,
|
|
20
|
+
length: len
|
|
21
|
+
});
|
|
22
|
+
adj.set(pipe.toNode, toList);
|
|
23
|
+
}
|
|
24
|
+
return adj;
|
|
25
|
+
}
|
|
26
|
+
function pipeLengthFromGeometry(pipe, nodes) {
|
|
27
|
+
if (pipe.length && pipe.length > 0) return pipe.length;
|
|
28
|
+
if (pipe.geometry && pipe.geometry.length >= 2) {
|
|
29
|
+
let total = 0;
|
|
30
|
+
for (let i = 1; i < pipe.geometry.length; i++) {
|
|
31
|
+
total += haversine(
|
|
32
|
+
pipe.geometry[i - 1][0],
|
|
33
|
+
pipe.geometry[i - 1][1],
|
|
34
|
+
pipe.geometry[i][0],
|
|
35
|
+
pipe.geometry[i][1]
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
return total;
|
|
39
|
+
}
|
|
40
|
+
const from = nodes.find((n) => n.id === pipe.fromNode);
|
|
41
|
+
const to = nodes.find((n) => n.id === pipe.toNode);
|
|
42
|
+
if (!from || !to) return 0;
|
|
43
|
+
return haversine(from.lng, from.lat, to.lng, to.lat);
|
|
44
|
+
}
|
|
45
|
+
function haversine(lng1, lat1, lng2, lat2) {
|
|
46
|
+
const R = 6371e3;
|
|
47
|
+
const toRad = (d) => d * Math.PI / 180;
|
|
48
|
+
const dLat = toRad(lat2 - lat1);
|
|
49
|
+
const dLng = toRad(lng2 - lng1);
|
|
50
|
+
const a = Math.sin(dLat / 2) ** 2 + Math.cos(toRad(lat1)) * Math.cos(toRad(lat2)) * Math.sin(dLng / 2) ** 2;
|
|
51
|
+
return 2 * R * Math.asin(Math.sqrt(a));
|
|
52
|
+
}
|
|
53
|
+
function neighborIds(adj, nodeId) {
|
|
54
|
+
const list = adj.get(nodeId) ?? [];
|
|
55
|
+
const seen = /* @__PURE__ */ new Set();
|
|
56
|
+
const out = [];
|
|
57
|
+
for (const e of list) {
|
|
58
|
+
if (!seen.has(e.to)) {
|
|
59
|
+
seen.add(e.to);
|
|
60
|
+
out.push(e.to);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return out;
|
|
64
|
+
}
|
|
65
|
+
function edgesOf(adj, nodeId) {
|
|
66
|
+
return adj.get(nodeId) ?? [];
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// src/graph/bfs.ts
|
|
70
|
+
function bfs(adj, start, opts = {}) {
|
|
71
|
+
const visited = [];
|
|
72
|
+
const parents = /* @__PURE__ */ new Map();
|
|
73
|
+
const visitedSet = /* @__PURE__ */ new Set();
|
|
74
|
+
if (!adj.has(start)) {
|
|
75
|
+
parents.set(start, null);
|
|
76
|
+
return { visited, parents, start };
|
|
77
|
+
}
|
|
78
|
+
if (opts.allowStart && !opts.allowStart(start)) {
|
|
79
|
+
return { visited: [], parents, start };
|
|
80
|
+
}
|
|
81
|
+
const queue = [{ id: start, depth: 0 }];
|
|
82
|
+
parents.set(start, null);
|
|
83
|
+
visitedSet.add(start);
|
|
84
|
+
visited.push(start);
|
|
85
|
+
let foundEnd;
|
|
86
|
+
while (queue.length > 0) {
|
|
87
|
+
const { id, depth } = queue.shift();
|
|
88
|
+
if (opts.until && opts.until(id)) {
|
|
89
|
+
foundEnd = id;
|
|
90
|
+
break;
|
|
91
|
+
}
|
|
92
|
+
if (opts.maxDepth !== void 0 && depth >= opts.maxDepth) continue;
|
|
93
|
+
const edges = adj.get(id) ?? [];
|
|
94
|
+
for (const e of edges) {
|
|
95
|
+
if (opts.allowEdge && !opts.allowEdge(e, id, e.to)) continue;
|
|
96
|
+
if (visitedSet.has(e.to)) continue;
|
|
97
|
+
visitedSet.add(e.to);
|
|
98
|
+
parents.set(e.to, id);
|
|
99
|
+
visited.push(e.to);
|
|
100
|
+
queue.push({ id: e.to, depth: depth + 1 });
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return { visited, parents, start, end: foundEnd };
|
|
104
|
+
}
|
|
105
|
+
function multiSourceBfs(adj, starts, opts = {}) {
|
|
106
|
+
const visited = [];
|
|
107
|
+
const parents = /* @__PURE__ */ new Map();
|
|
108
|
+
const sourceMap = /* @__PURE__ */ new Map();
|
|
109
|
+
const visitedSet = /* @__PURE__ */ new Set();
|
|
110
|
+
const queue = [];
|
|
111
|
+
for (const s of starts) {
|
|
112
|
+
if (!adj.has(s)) continue;
|
|
113
|
+
if (opts.allowStart && !opts.allowStart(s)) continue;
|
|
114
|
+
if (!visitedSet.has(s)) {
|
|
115
|
+
visitedSet.add(s);
|
|
116
|
+
parents.set(s, null);
|
|
117
|
+
sourceMap.set(s, s);
|
|
118
|
+
visited.push(s);
|
|
119
|
+
queue.push({ id: s, depth: 0 });
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
while (queue.length > 0) {
|
|
123
|
+
const { id, depth } = queue.shift();
|
|
124
|
+
if (opts.maxDepth !== void 0 && depth >= opts.maxDepth) continue;
|
|
125
|
+
const edges = adj.get(id) ?? [];
|
|
126
|
+
for (const e of edges) {
|
|
127
|
+
if (opts.allowEdge && !opts.allowEdge(e, id, e.to)) continue;
|
|
128
|
+
if (visitedSet.has(e.to)) continue;
|
|
129
|
+
visitedSet.add(e.to);
|
|
130
|
+
parents.set(e.to, id);
|
|
131
|
+
sourceMap.set(e.to, sourceMap.get(id));
|
|
132
|
+
visited.push(e.to);
|
|
133
|
+
queue.push({ id: e.to, depth: depth + 1 });
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
return { visited, parents, start: starts[0] ?? "", sourceMap };
|
|
137
|
+
}
|
|
138
|
+
function reconstructPath(parents, end) {
|
|
139
|
+
const path = [];
|
|
140
|
+
let cur = end;
|
|
141
|
+
while (cur) {
|
|
142
|
+
path.unshift(cur);
|
|
143
|
+
const p = parents.get(cur);
|
|
144
|
+
cur = p ?? null;
|
|
145
|
+
if (cur === null && p === null) {
|
|
146
|
+
if (parents.get(end) === null && path.length === 1) break;
|
|
147
|
+
break;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
if (path[0] !== end) ;
|
|
151
|
+
return path;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// src/graph/connectivity.ts
|
|
155
|
+
function findConnectedComponents(adj, nodes) {
|
|
156
|
+
const visited = /* @__PURE__ */ new Set();
|
|
157
|
+
const components = [];
|
|
158
|
+
const kindById = /* @__PURE__ */ new Map();
|
|
159
|
+
for (const n of nodes) kindById.set(n.id, n.kind);
|
|
160
|
+
for (const nodeId of adj.keys()) {
|
|
161
|
+
if (visited.has(nodeId)) continue;
|
|
162
|
+
const r = bfs(adj, nodeId);
|
|
163
|
+
let hasSource = false;
|
|
164
|
+
for (const id of r.visited) {
|
|
165
|
+
visited.add(id);
|
|
166
|
+
if (kindById.get(id) === "source") hasSource = true;
|
|
167
|
+
}
|
|
168
|
+
components.push({
|
|
169
|
+
nodes: r.visited,
|
|
170
|
+
size: r.visited.length,
|
|
171
|
+
hasSource
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
return components;
|
|
175
|
+
}
|
|
176
|
+
function findPaths(adj, start, end, opts = {}) {
|
|
177
|
+
const maxPaths = opts.maxPaths ?? 3;
|
|
178
|
+
const maxDepth = opts.maxDepth ?? 20;
|
|
179
|
+
const all = [];
|
|
180
|
+
const path = [];
|
|
181
|
+
const onPath = /* @__PURE__ */ new Set();
|
|
182
|
+
const dfsLocal = (cur) => {
|
|
183
|
+
if (all.length >= maxPaths) return;
|
|
184
|
+
if (cur === end) {
|
|
185
|
+
all.push([...path]);
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
if (path.length >= maxDepth) return;
|
|
189
|
+
onPath.add(cur);
|
|
190
|
+
path.push(cur);
|
|
191
|
+
const edges = adj.get(cur) ?? [];
|
|
192
|
+
for (const e of edges) {
|
|
193
|
+
if (onPath.has(e.to)) continue;
|
|
194
|
+
dfsLocal(e.to);
|
|
195
|
+
if (all.length >= maxPaths) return;
|
|
196
|
+
}
|
|
197
|
+
path.pop();
|
|
198
|
+
onPath.delete(cur);
|
|
199
|
+
};
|
|
200
|
+
dfsLocal(start);
|
|
201
|
+
return all;
|
|
202
|
+
}
|
|
203
|
+
function findUpstreamNode(adj, startPipeId, dataset, predicate) {
|
|
204
|
+
const pipe = dataset.pipes.find((p) => p.id === startPipeId);
|
|
205
|
+
if (!pipe) return null;
|
|
206
|
+
const nodeById = new Map(dataset.nodes.map((n) => [n.id, n]));
|
|
207
|
+
const startId = pipe.fromNode;
|
|
208
|
+
const startNode = nodeById.get(startId);
|
|
209
|
+
if (!startNode) return null;
|
|
210
|
+
const allowEdge = (e, _from, to) => {
|
|
211
|
+
const targetPipe = dataset.pipes.find((p) => p.id === e.pipeId);
|
|
212
|
+
if (!targetPipe) return false;
|
|
213
|
+
return true;
|
|
214
|
+
};
|
|
215
|
+
const r = bfs(adj, startId, {
|
|
216
|
+
allowEdge,
|
|
217
|
+
until: (n) => {
|
|
218
|
+
const node = nodeById.get(n);
|
|
219
|
+
return node ? predicate(node) : false;
|
|
220
|
+
},
|
|
221
|
+
maxDepth: 50
|
|
222
|
+
});
|
|
223
|
+
if (!r.end) return null;
|
|
224
|
+
return nodeById.get(r.end) ?? null;
|
|
225
|
+
}
|
|
226
|
+
function highlightConnectivity(adj, start) {
|
|
227
|
+
const r = bfs(adj, start, { maxDepth: 64 });
|
|
228
|
+
const nodeIds = new Set(r.visited);
|
|
229
|
+
const edgeIds = /* @__PURE__ */ new Set();
|
|
230
|
+
for (const n of r.visited) {
|
|
231
|
+
const edges = adj.get(n) ?? [];
|
|
232
|
+
for (const e of edges) {
|
|
233
|
+
if (nodeIds.has(e.to)) edgeIds.add(e.pipeId);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
return { nodeIds, edgeIds };
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// src/graph/shortestPath.ts
|
|
240
|
+
function dijkstra(adj, start, end, opts = {}) {
|
|
241
|
+
if (!adj.has(start) || !adj.has(end)) {
|
|
242
|
+
return { distance: Infinity, path: [], found: false };
|
|
243
|
+
}
|
|
244
|
+
const weight = opts.weight ?? ((e) => e.length || 1);
|
|
245
|
+
const dist = /* @__PURE__ */ new Map();
|
|
246
|
+
const parents = /* @__PURE__ */ new Map();
|
|
247
|
+
const visited = /* @__PURE__ */ new Set();
|
|
248
|
+
const pq = [{ id: start, d: 0 }];
|
|
249
|
+
for (const n of adj.keys()) {
|
|
250
|
+
dist.set(n, Infinity);
|
|
251
|
+
parents.set(n, null);
|
|
252
|
+
}
|
|
253
|
+
dist.set(start, 0);
|
|
254
|
+
while (pq.length > 0) {
|
|
255
|
+
pq.sort((a, b) => a.d - b.d);
|
|
256
|
+
const cur2 = pq.shift();
|
|
257
|
+
if (visited.has(cur2.id)) continue;
|
|
258
|
+
visited.add(cur2.id);
|
|
259
|
+
if (cur2.id === end) break;
|
|
260
|
+
const edges = adj.get(cur2.id) ?? [];
|
|
261
|
+
for (const e of edges) {
|
|
262
|
+
if (visited.has(e.to)) continue;
|
|
263
|
+
const w = weight(e);
|
|
264
|
+
const alt = (dist.get(cur2.id) ?? Infinity) + w;
|
|
265
|
+
if (alt < (dist.get(e.to) ?? Infinity)) {
|
|
266
|
+
dist.set(e.to, alt);
|
|
267
|
+
parents.set(e.to, cur2.id);
|
|
268
|
+
pq.push({ id: e.to, d: alt });
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
if (opts.until && opts.until(cur2.id, cur2.d)) break;
|
|
272
|
+
}
|
|
273
|
+
if ((dist.get(end) ?? Infinity) === Infinity) {
|
|
274
|
+
return { distance: Infinity, path: [], found: false };
|
|
275
|
+
}
|
|
276
|
+
const path = [];
|
|
277
|
+
let cur = end;
|
|
278
|
+
while (cur) {
|
|
279
|
+
path.unshift(cur);
|
|
280
|
+
const p = parents.get(cur);
|
|
281
|
+
if (p === void 0) break;
|
|
282
|
+
cur = p;
|
|
283
|
+
}
|
|
284
|
+
return { distance: dist.get(end) ?? 0, path, found: true };
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
export { bfs, buildAdjacency, dijkstra, edgesOf, findConnectedComponents, findPaths, findUpstreamNode, haversine, highlightConnectivity, multiSourceBfs, neighborIds, pipeLengthFromGeometry, reconstructPath };
|
|
288
|
+
//# sourceMappingURL=chunk-GCDA7KJM.js.map
|
|
289
|
+
//# sourceMappingURL=chunk-GCDA7KJM.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/graph/adjacency.ts","../src/graph/bfs.ts","../src/graph/connectivity.ts","../src/graph/shortestPath.ts"],"names":["cur"],"mappings":"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","file":"chunk-GCDA7KJM.js","sourcesContent":["/**\n * 图邻接表构建(管线拓扑 → 双向邻接表)\n *\n * 输入:管网拓扑数据集(节点 + 管段)\n * 输出:邻接表 Map<nodeId, Edge[]>\n *\n * Phase 1 图遍历的入口:把物理拓扑转换为图论可用的数据结构。\n */\n\nimport type {\n PipelineNode,\n PipelinePipe,\n PipelineTopologyDataset,\n} from '../types';\n\n/**\n * 邻接表中的\"边\"\n * - 物理上一条 pipe 是两个 node 之间的连接\n * - 邻接表中记录:pipeId、邻居节点 id、长度等\n */\nexport interface AdjEdge {\n pipeId: string;\n /** 邻居节点 id(沿此边走一步到达) */\n to: string;\n /** 长度(m),无则 0 */\n length: number;\n /** 阻力/权重(用于复杂路径优选,可选) */\n weight?: number;\n}\n\n/** 邻接表:Map<nodeId, AdjEdge[]> */\nexport type AdjacencyList = Map<string, AdjEdge[]>;\n\n/**\n * 从 PipelineTopologyDataset 构建邻接表\n */\nexport function buildAdjacency(dataset: PipelineTopologyDataset): AdjacencyList {\n const adj: AdjacencyList = new Map();\n\n // 初始化所有节点(防止孤立点不出现)\n for (const node of dataset.nodes) {\n if (!adj.has(node.id)) adj.set(node.id, []);\n }\n\n for (const pipe of dataset.pipes) {\n const len = pipe.length ?? pipeLengthFromGeometry(pipe, dataset.nodes) ?? 0;\n\n const fromList = adj.get(pipe.fromNode) ?? [];\n fromList.push({\n pipeId: pipe.id,\n to: pipe.toNode,\n length: len,\n });\n adj.set(pipe.fromNode, fromList);\n\n const toList = adj.get(pipe.toNode) ?? [];\n toList.push({\n pipeId: pipe.id,\n to: pipe.fromNode,\n length: len,\n });\n adj.set(pipe.toNode, toList);\n }\n\n return adj;\n}\n\n/**\n * 计算单条 pipe 长度(haversine)\n * 若 pipe 提供 geometry 则累计几何点距离,否则按 from-to 直线长度。\n */\nexport function pipeLengthFromGeometry(\n pipe: PipelinePipe,\n nodes: PipelineNode[]\n): number {\n if (pipe.length && pipe.length > 0) return pipe.length;\n if (pipe.geometry && pipe.geometry.length >= 2) {\n let total = 0;\n for (let i = 1; i < pipe.geometry.length; i++) {\n total += haversine(\n pipe.geometry[i - 1][0],\n pipe.geometry[i - 1][1],\n pipe.geometry[i][0],\n pipe.geometry[i][1]\n );\n }\n return total;\n }\n const from = nodes.find((n) => n.id === pipe.fromNode);\n const to = nodes.find((n) => n.id === pipe.toNode);\n if (!from || !to) return 0;\n return haversine(from.lng, from.lat, to.lng, to.lat);\n}\n\n/** Haversine 距离(m) */\nexport function haversine(\n lng1: number,\n lat1: number,\n lng2: number,\n lat2: number\n): number {\n const R = 6_371_000;\n const toRad = (d: number) => (d * Math.PI) / 180;\n const dLat = toRad(lat2 - lat1);\n const dLng = toRad(lng2 - lng1);\n const a =\n Math.sin(dLat / 2) ** 2 +\n Math.cos(toRad(lat1)) *\n Math.cos(toRad(lat2)) *\n Math.sin(dLng / 2) ** 2;\n return 2 * R * Math.asin(Math.sqrt(a));\n}\n\n/** 从邻接表拿指定节点的邻居 id 列表(去重) */\nexport function neighborIds(adj: AdjacencyList, nodeId: string): string[] {\n const list = adj.get(nodeId) ?? [];\n const seen = new Set<string>();\n const out: string[] = [];\n for (const e of list) {\n if (!seen.has(e.to)) {\n seen.add(e.to);\n out.push(e.to);\n }\n }\n return out;\n}\n\n/** 取出指定节点的所有边 */\nexport function edgesOf(adj: AdjacencyList, nodeId: string): AdjEdge[] {\n return adj.get(nodeId) ?? [];\n}\n","/**\n * BFS(广度优先遍历)\n *\n * 用途:\n * - 爆管推演:下游影响范围(沿拓扑扩散)\n * - 连通性查询:在指定层级内查找可达节点\n *\n * 实现要点:\n * - 支持 visited / parents 记录\n * - 支持 maxDepth 限制(避免极大连通分量过长)\n * - 支持 predicate 过滤可继续遍历的邻居\n * - 可返回\"沿途经过的管段\"列表(用于前端高亮)\n */\n\nimport type { AdjacencyList, AdjEdge } from './adjacency';\n\nexport interface BfsResult {\n /** 访问过的节点(不重复) */\n visited: string[];\n /** 父节点表:node -> 上一步到达的 node(首节点为自身) */\n parents: Map<string, string | null>;\n /** 起点 节点 */\n start: string;\n /** 终点节点(搜到即停,返回时 visited 是到终点为止的节点) */\n end?: string;\n}\n\nexport interface BfsOptions {\n /** 最大深度(边数),默认无限制 */\n maxDepth?: number;\n /** 是否指定终点(BFS 找到则停止) */\n until?: (node: string) => boolean;\n /** 在扩展邻居前过滤:false 则该方向不继续遍历 */\n allowEdge?: (edge: AdjEdge, from: string, to: string) => boolean;\n /** 起点本身的过滤器(如只走下游/上游) */\n allowStart?: (node: string) => boolean;\n}\n\n/**\n * 标准 BFS,可指定\"任意邻居是否可达\"\n *\n * 用法:\n * const r = bfs(adj, 'node-A');\n * bfs(adj, 'start', { until: (n) => n === 'target' }); // 找终点\n * bfs(adj, 'start', { allowEdge: (_, __, to) => to !== 'forbidden' });\n */\nexport function bfs(\n adj: AdjacencyList,\n start: string,\n opts: BfsOptions = {}\n): BfsResult {\n const visited: string[] = [];\n const parents = new Map<string, string | null>();\n const visitedSet = new Set<string>();\n\n if (!adj.has(start)) {\n parents.set(start, null);\n return { visited, parents, start };\n }\n\n if (opts.allowStart && !opts.allowStart(start)) {\n return { visited: [], parents, start };\n }\n\n const queue: Array<{ id: string; depth: number }> = [{ id: start, depth: 0 }];\n parents.set(start, null);\n visitedSet.add(start);\n visited.push(start);\n\n let foundEnd: string | undefined;\n while (queue.length > 0) {\n const { id, depth } = queue.shift()!;\n\n if (opts.until && opts.until(id)) {\n foundEnd = id;\n break;\n }\n\n // maxDepth 语义:最多走 N 条边,到达节点的最短路径长度为 N\n // 当前节点 depth=d = 经过 d 条边到达,扩展邻居须 depth+1 <= maxDepth\n if (opts.maxDepth !== undefined && depth >= opts.maxDepth) continue;\n\n const edges = adj.get(id) ?? [];\n for (const e of edges) {\n if (opts.allowEdge && !opts.allowEdge(e, id, e.to)) continue;\n if (visitedSet.has(e.to)) continue;\n visitedSet.add(e.to);\n parents.set(e.to, id);\n visited.push(e.to);\n queue.push({ id: e.to, depth: depth + 1 });\n }\n }\n\n return { visited, parents, start, end: foundEnd };\n}\n\n/**\n * 多源 BFS(同时从多个起点扩散)\n *\n * 用法:爆管分析的下游影响可从多点同时推演(如多个隔离阀门的下游剩余服务区)\n */\nexport function multiSourceBfs(\n adj: AdjacencyList,\n starts: string[],\n opts: BfsOptions = {}\n): BfsResult & { sourceMap: Map<string, string> } {\n const visited: string[] = [];\n const parents = new Map<string, string | null>();\n const sourceMap = new Map<string, string>();\n const visitedSet = new Set<string>();\n\n const queue: Array<{ id: string; depth: number }> = [];\n\n for (const s of starts) {\n if (!adj.has(s)) continue;\n if (opts.allowStart && !opts.allowStart(s)) continue;\n if (!visitedSet.has(s)) {\n visitedSet.add(s);\n parents.set(s, null);\n sourceMap.set(s, s);\n visited.push(s);\n queue.push({ id: s, depth: 0 });\n }\n }\n\n while (queue.length > 0) {\n const { id, depth } = queue.shift()!;\n // 同单源 BFS:depth=d 时扩展邻居;maxDepth=N 表示最多 N 条边\n if (opts.maxDepth !== undefined && depth >= opts.maxDepth) continue;\n\n const edges = adj.get(id) ?? [];\n for (const e of edges) {\n if (opts.allowEdge && !opts.allowEdge(e, id, e.to)) continue;\n if (visitedSet.has(e.to)) continue;\n visitedSet.add(e.to);\n parents.set(e.to, id);\n sourceMap.set(e.to, sourceMap.get(id)!);\n visited.push(e.to);\n queue.push({ id: e.to, depth: depth + 1 });\n }\n }\n\n return { visited, parents, start: starts[0] ?? '', sourceMap };\n}\n\n/**\n * 反向 BFS(沿父指针回溯)\n *\n * 给定终点 + parents Map,可还原 BFS 路径(用于\"找到的最短路径\")。\n */\nexport function reconstructPath(\n parents: Map<string, string | null>,\n end: string\n): string[] {\n const path: string[] = [];\n let cur: string | null = end;\n while (cur) {\n path.unshift(cur);\n const p = parents.get(cur);\n cur = p ?? null;\n if (cur === null && p === null) {\n // 起点到达\n if (parents.get(end) === null && path.length === 1) break;\n break;\n }\n }\n if (path[0] !== end) {\n // already include end correctly\n }\n return path;\n}\n","/**\n * 连通性分析\n *\n * 用途:\n * - P-4 连通性高亮:选中管线后高亮上下游连通路径\n * - 健康评估的影响范围计算\n * - 用户问\"阀门X关闭后影响哪些小区?\"\n */\n\nimport type { AdjacencyList, AdjEdge } from './adjacency';\nimport { bfs } from './bfs';\nimport type { PipelineNode, PipelineTopologyDataset, NodeKind } from '../types';\n\n/**\n * 连通分量(连通子图)\n */\nexport interface ConnectedComponent {\n /** 节点集合 */\n nodes: string[];\n /** 节点数 */\n size: number;\n /** 是否包含 source 类型节点(影响气源/水源判定) */\n hasSource: boolean;\n}\n\n/**\n * 求所有连通分量\n */\nexport function findConnectedComponents(\n adj: AdjacencyList,\n nodes: PipelineNode[]\n): ConnectedComponent[] {\n const visited = new Set<string>();\n const components: ConnectedComponent[] = [];\n\n const kindById = new Map<string, NodeKind>();\n for (const n of nodes) kindById.set(n.id, n.kind);\n\n for (const nodeId of adj.keys()) {\n if (visited.has(nodeId)) continue;\n const r = bfs(adj, nodeId);\n let hasSource = false;\n for (const id of r.visited) {\n visited.add(id);\n if (kindById.get(id) === 'source') hasSource = true;\n }\n components.push({\n nodes: r.visited,\n size: r.visited.length,\n hasSource,\n });\n }\n\n return components;\n}\n\n/**\n * 找到两个节点之间的所有路径(k 短路)\n *\n * 用途:阀门隔离方案中\"备选路径\"分析\n * NOTE:DFS+Yen's 算法简化版,路径数 < 10 适用。\n */\nexport function findPaths(\n adj: AdjacencyList,\n start: string,\n end: string,\n opts: { maxPaths?: number; maxDepth?: number } = {}\n): string[][] {\n const maxPaths = opts.maxPaths ?? 3;\n const maxDepth = opts.maxDepth ?? 20;\n\n const all: string[][] = [];\n const path: string[] = [];\n const onPath = new Set<string>();\n\n const dfsLocal = (cur: string) => {\n if (all.length >= maxPaths) return;\n if (cur === end) {\n all.push([...path]);\n return;\n }\n if (path.length >= maxDepth) return;\n onPath.add(cur);\n path.push(cur);\n const edges = adj.get(cur) ?? [];\n for (const e of edges) {\n if (onPath.has(e.to)) continue;\n dfsLocal(e.to);\n if (all.length >= maxPaths) return;\n }\n path.pop();\n onPath.delete(cur);\n };\n\n dfsLocal(start);\n return all;\n}\n\n/**\n * 沿拓扑向上游找到最近的指定类型节点(默认阀门)\n *\n * 用途:爆管推演自动定位隔离点(PRD §4.2.4 step 1)\n */\nexport function findUpstreamNode(\n adj: AdjacencyList,\n startPipeId: string,\n dataset: PipelineTopologyDataset,\n predicate: (n: PipelineNode) => boolean\n): PipelineNode | null {\n const pipe = dataset.pipes.find((p) => p.id === startPipeId);\n if (!pipe) return null;\n\n const nodeById = new Map(dataset.nodes.map((n) => [n.id, n]));\n\n // 起始:pipe 任意一端的\"非故障方向\" → 选择 fromNode 作为查找起点\n const startId = pipe.fromNode;\n const startNode = nodeById.get(startId);\n if (!startNode) return null;\n\n // 反向游走:只允许沿\"反向\"(即 from 是 to,to 是 from)前进\n const allowEdge = (e: AdjEdge, _from: string, to: string) => {\n // 边界:pipe 同时含 startId 端则允许从 startId 反向走\n const targetPipe = dataset.pipes.find((p) => p.id === e.pipeId);\n if (!targetPipe) return false;\n // 始终允许走(无向图)\n return true;\n };\n\n // BFS 到上层直到命中 predicate\n const r = bfs(adj, startId, {\n allowEdge,\n until: (n) => {\n const node = nodeById.get(n);\n return node ? predicate(node) : false;\n },\n maxDepth: 50,\n });\n\n if (!r.end) return null;\n return nodeById.get(r.end) ?? null;\n}\n\n/**\n * 高亮连通路径(从起点 BFS,把沿途所有 pipe 收集起来)\n *\n * 用途:选中管段/节点 → 高亮上下游\n */\nexport function highlightConnectivity(\n adj: AdjacencyList,\n start: string\n): { nodeIds: Set<string>; edgeIds: Set<string> } {\n const r = bfs(adj, start, { maxDepth: 64 });\n const nodeIds = new Set(r.visited);\n const edgeIds = new Set<string>();\n\n for (const n of r.visited) {\n const edges = adj.get(n) ?? [];\n for (const e of edges) {\n // 只高亮两端都已访问的边\n if (nodeIds.has(e.to)) edgeIds.add(e.pipeId);\n }\n }\n\n return { nodeIds, edgeIds };\n}\n","/**\n * 最短路径(Dijkstra)\n *\n * 用途:\n * - 上游距离最近的阀门定位\n * - 备选供气/供水路径(按管径/压力选优)\n */\n\nimport type { AdjacencyList, AdjEdge } from './adjacency';\n\nexport interface ShortestPathResult {\n distance: number;\n path: string[];\n found: boolean;\n}\n\nexport interface ShortestPathOptions {\n /** 边权重函数:默认 1 */\n weight?: (edge: AdjEdge) => number;\n /** 提前终止:找到满足条件的节点立即返回 */\n until?: (node: string, dist: number) => boolean;\n}\n\n/**\n * Dijkstra 最短路径\n *\n * 注意:当存在负权边时不可用;管网场景边权重非负,管长/管径均适用。\n */\nexport function dijkstra(\n adj: AdjacencyList,\n start: string,\n end: string,\n opts: ShortestPathOptions = {}\n): ShortestPathResult {\n if (!adj.has(start) || !adj.has(end)) {\n return { distance: Infinity, path: [], found: false };\n }\n\n const weight = opts.weight ?? ((e: AdjEdge) => e.length || 1);\n\n const dist = new Map<string, number>();\n const parents = new Map<string, string | null>();\n const visited = new Set<string>();\n const pq: Array<{ id: string; d: number }> = [{ id: start, d: 0 }];\n\n for (const n of adj.keys()) {\n dist.set(n, Infinity);\n parents.set(n, null);\n }\n dist.set(start, 0);\n\n while (pq.length > 0) {\n // 简化:线性查找最小值(小图足够;大规模可换二叉堆)\n pq.sort((a, b) => a.d - b.d);\n const cur = pq.shift()!;\n if (visited.has(cur.id)) continue;\n visited.add(cur.id);\n if (cur.id === end) break;\n\n const edges = adj.get(cur.id) ?? [];\n for (const e of edges) {\n if (visited.has(e.to)) continue;\n const w = weight(e);\n const alt = (dist.get(cur.id) ?? Infinity) + w;\n if (alt < (dist.get(e.to) ?? Infinity)) {\n dist.set(e.to, alt);\n parents.set(e.to, cur.id);\n pq.push({ id: e.to, d: alt });\n }\n }\n\n if (opts.until && opts.until(cur.id, cur.d)) break;\n }\n\n if ((dist.get(end) ?? Infinity) === Infinity) {\n return { distance: Infinity, path: [], found: false };\n }\n\n // 回溯路径\n const path: string[] = [];\n let cur: string | null = end;\n while (cur) {\n path.unshift(cur);\n const p = parents.get(cur);\n if (p === undefined) break;\n cur = p;\n }\n\n return { distance: dist.get(end) ?? 0, path, found: true };\n}\n"]}
|