Ver Fonte

fix(cockpit): use category-specific virtual pipe depths

ChenJZ há 5 dias atrás
pai
commit
7a09b7cbe5

+ 2 - 2
CONTEXT.md

@@ -43,8 +43,8 @@ _Avoid_: 真实管径、用显示尺寸回写业务管径
 _Avoid_: 实测管径、默认管径、用推算尺寸证明安装拓扑
 
 **虚拟埋深**:
-为地下可视化明确指定的地表以下距离,不是测量或后端发布的真实管道埋深。
-_Avoid_: 实测埋深、真实高程
+为地下可视化明确指定的地表以下距离,不是测量或后端发布的真实管道埋深。当前示意默认:燃气1.5m、供水4m、污水8m、雨水10m;非法排水等级使用8m灰色占位。
+_Avoid_: 实测埋深、真实高程、把类别默认埋深写回后端
 
 **关联监测设备图层**:
 以当前所选监测点为归属、按一级模式显示其关联监测设备的空间图层;它是显示概念,不是独立业务对象,也不改变未关联设备的身份。

+ 1 - 1
public/cockpit/models/pipes/README.md

@@ -10,7 +10,7 @@
 
 以视图模式控制遮挡:地面视图在所有距离始终保留4px同色2D导引线;管线视图只有3D实例就绪后才对近景导引线施加600m下限,远景保留导引。未就绪或失败时导引全距离显示。视图切换即时更新可见性,不重建模型批次。管线视图默认地形和建筑alpha0.18、pitch-35°、距离系数0.2;地面视图仍恢复不透明。
 
-构件是标准示意件,最大主体跨度约 1m。它们不代表厂家型号、真实弯曲角度、安装尺寸或真实朝向。构件朝向固定,仅用于类型辨认。模型中心/中心线使用地表采样高程减 **虚拟埋深 2m**;这不是后端实测埋深。
+构件是标准示意件,最大主体跨度约 1m。它们不代表厂家型号、真实弯曲角度、安装尺寸或真实朝向。构件朝向固定,仅用于类型辨认。管段模型中心/中心线使用地表采样高程减类别虚拟埋深:燃气1.5m、供水4m、污水8m、雨水10m;非法排水等级按8m灰色占位。以上都不是后端实测埋深。
 
 缺少管径时,先在同一行业、端点坐标完全重合且无分叉的连续链上找两侧已有管径;在两个锚点之间按缺失段路径中点的长度比例插值(同值锚点直接沿用)。这属于推算管径,仅用于模型示意,保留两侧编码/管径来源,不覆盖后端源数据;不是安装拓扑的证明。分叉、只有单侧锚点、闭环只有同一个锚点、无锚点或端点仅邻近时均不补可靠管径。此时仅为显示使用分类默认:燃气DN160、供水DN400、污水/雨水DN800;非法等级排水也使用共同DN800但保持灰色。`diameterM`仍为null、来源标记`visual-default`,单独保留`visualDefaultDiameterM`,再按现有8倍/1.5–3m规则得到最终显示粗度。默认尺寸不参与插值锚点,也不回写数据库。未知行业不猜,继续保留折线。
 

+ 6 - 5
src/modules/cockpit/domain/pipeModels.js

@@ -1,11 +1,11 @@
 import { displayDiameterFor } from './pipeDiameterDisplay.js'
 import { resolvePipeDiameters } from './pipeDiameter.js'
-import { displayPipeDiameter, pipeColorKey } from './pipeVisual.js'
+import { displayPipeDiameter, pipeColorKey, pipeVirtualDepthM, PIPE_VIRTUAL_DEPTH_M } from './pipeVisual.js'
 
 /** 管体是沿真实折线路径的体积示意;虚拟埋深不是业务实测值。 */
 export const PIPE_MODEL_POLICY = Object.freeze({ virtualDepthM: 2, componentSizeM: 1 })
 export const PIPE_MODEL_KINDS = ['spline', 'elbow', 'valve', 'branch', 'cross']
-export const PIPE_MODEL_NOTE = '管体示意放大8倍、限制1.5–3m(非工程尺寸);源/双侧推算优先,仍缺管径时仅显示默认:燃气DN160、供水DN400、污水/雨水及雨污未分类排水DN800,未知行业不猜;源管径不回写。地面视图所有距离保留彩色导引,管线视图已建模段600m外保留导引;虚拟埋深2m(非实测);燃气红、供水蓝、污水绿、雨水青、窨井紫,雨污按network_level:0污水、1雨水、缺失按0,其它值未分类;构件固定示意朝向,素材/地表失败保留折线或符号。'
+export const PIPE_MODEL_NOTE = '管体示意放大8倍、限制1.5–3m(非工程尺寸);虚拟埋深按类别:燃气1.5m、供水4m、污水8m、雨水10m(均非实测);源/双侧推算优先,缺径仅显示默认DN160/400/800/800,不回写源管径。地面视图所有距离保留彩色导引,管线视图已建模段600m外保留导引;燃气红、供水蓝、污水绿、雨水青、窨井紫,雨污按network_level:0污水、1雨水、缺失按0,其它值未分类;构件固定示意朝向,素材/地表失败保留折线或符号。'
 
 /** 输入只接收 toPipeNetwork 已准入且经模式计划裁剪后的对象,不再推断坐标/拓扑。 */
 export function planPipeModels(network = {}) {
@@ -26,7 +26,7 @@ export function planPipeModels(network = {}) {
         if (!diameter) visualDefaultCount += 1
         const diameterM = diameter ? diameter.diameterMm / 1000 : null
         for (let i = 1; i < pipe.positions.length; i += 1) {
-            instances.push({ id, kind: 'spline', industry: pipe.industry, colorKey, start: pipe.positions[i - 1], end: pipe.positions[i], diameterM, displayDiameterM:displayPipeDiameter(diameterM ?? visualDefaultDiameterM), diameterSource:diameter?.source || 'visual-default', diameterAnchors:diameter?.anchors || [], visualDefaultDiameterM })
+            instances.push({ id, kind: 'spline', industry: pipe.industry, colorKey, virtualDepthM:pipeVirtualDepthM(colorKey), start: pipe.positions[i - 1], end: pipe.positions[i], diameterM, displayDiameterM:displayPipeDiameter(diameterM ?? visualDefaultDiameterM), diameterSource:diameter?.source || 'visual-default', diameterAnchors:diameter?.anchors || [], visualDefaultDiameterM })
         }
     }
     for (const component of network.components || []) {
@@ -36,12 +36,13 @@ export function planPipeModels(network = {}) {
             continue
         }
         const kind = component.kind === 'branch' && component.label === 'cross' ? 'cross' : component.kind
-        instances.push({ id, kind, industry: component.industry, colorKey:pipeColorKey(component), position: component.position, sizeM: PIPE_MODEL_POLICY.componentSizeM, orientationSource: 'fixed-approximate' })
+        const colorKey = pipeColorKey(component)
+        instances.push({ id, kind, industry: component.industry, colorKey, virtualDepthM:pipeVirtualDepthM(colorKey), position: component.position, sizeM: PIPE_MODEL_POLICY.componentSizeM, orientationSource: 'fixed-approximate' })
     }
     const diameterSummary = { reported:0, interpolated:0, missing:0 }
     for (const pipe of pipes) {
         const diameter = diameters.get(pipe)
         diameterSummary[diameter ? diameter.source : 'missing'] += 1
     }
-    return { instances, fallbacks, diameterSummary, visualDefaultCount, virtualDepthM: PIPE_MODEL_POLICY.virtualDepthM }
+    return { instances, fallbacks, diameterSummary, visualDefaultCount, virtualDepthM: null, virtualDepthByColor: PIPE_VIRTUAL_DEPTH_M }
 }

+ 4 - 0
src/modules/cockpit/domain/pipeVisual.js

@@ -9,6 +9,10 @@ export const PIPE_PALETTE = Object.freeze({
     UNKNOWN: { color:'#aab7c4', label:'未分类' },
 })
 export const PIPE_DISPLAY = Object.freeze({ diameterScale:8, minDiameterM:1.5, maxDiameterM:3, guideFromM:600, guideWidthPx:4 })
+export const PIPE_VIRTUAL_DEPTH_M = Object.freeze({ GAS:1.5, WATER_SUPPLY:4, SEWAGE:8, RAINWATER:10, DRAINAGE_UNKNOWN:8 })
+export function pipeVirtualDepthM(colorKey) {
+    return PIPE_VIRTUAL_DEPTH_M[colorKey] ?? 2
+}
 
 export function pipeColorKey(row) {
     if (row.industry !== 'DRAINAGE') return ['GAS','WATER_SUPPLY','MANHOLE'].includes(row.industry) ? row.industry : 'UNKNOWN'

+ 3 - 3
src/modules/cockpit/services/cesiumPipeModels.js

@@ -70,9 +70,9 @@ async function groupsFor(viewer, plan, heights, alive) {
             for(let j=0;j<chunk.length;j+=1) if(Number.isFinite(values[j]?.height)) heights.set(chunk[j][0],values[j].height)
         } catch { /* No invented ground elevation: retain fallback. */ }
     }
-    const world=(p)=>{
+    const world=(p,depthM=2)=>{
         const height=heights.get(keyOf(p))
-        return Number.isFinite(height)?Cartesian3.fromDegrees(p[0],p[1],height-plan.virtualDepthM):null
+        return Number.isFinite(height)?Cartesian3.fromDegrees(p[0],p[1],height-depthM):null
     }
     const groups=new Map(), missing=new Set()
     for(const row of rows) {
@@ -82,7 +82,7 @@ async function groupsFor(viewer, plan, heights, alive) {
         const origin=[(tileX+.5)*TILE_DEG,(tileY+.5)*TILE_DEG]
         const matrix=Transforms.eastNorthUpToFixedFrame(Cartesian3.fromDegrees(...origin))
         const inverse=Matrix4.inverseTransformation(matrix,new Matrix4())
-        const a=world(p),b=row.end?world(row.end):null
+        const a=world(p,row.virtualDepthM),b=row.end?world(row.end,row.virtualDepthM):null
         if(!a||(row.end&&!b)){missing.add(row.id);continue}
         const local=Matrix4.multiplyByPoint(inverse,a,new Cartesian3())
         let translation=gltfCoordinates(local),rotation=[0,0,0,1],scale=[row.sizeM,row.sizeM,row.sizeM]

+ 1 - 1
src/modules/cockpit/services/mapService.js

@@ -2531,7 +2531,7 @@ export async function syncPipeLayerForPlan(plan) {
             host.dataset.mapPipeModelPending = String(state.pending)
             host.dataset.mapPipeModelFailed = String(state.failed)
             host.dataset.mapPipeModelKinds = JSON.stringify(state.byKind)
-            host.dataset.mapPipeVirtualDepth = '2'
+            host.dataset.mapPipeVirtualDepth = 'category-default'
         }
         host.dispatchEvent(new CustomEvent('cockpit-pipe-model-state', { detail: state }))
     } })