| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114 |
- /** Facility selection uses entity category+primary key, never monitoring point codes. */
- import { facilityStatusTone, entityIdentity } from './entityDetail.js'
- import { COORDINATE_REASON_TEXT, NO_MAP_LOCATION_TEXT } from './coordinate.js'
- import { filterFacilityPoints, FACILITY_DETAIL_CATEGORIES } from './facilityPoints.js'
- export function facilityIdentity(input) {
- const identity = entityIdentity(input, 'facility')
- return { ...identity, valid: identity.valid && identity.kind === 'facility' && FACILITY_DETAIL_CATEGORIES.includes(identity.category) }
- }
- const coordinatesOf = value => value && Number.isFinite(value.longitude) && Number.isFinite(value.latitude)
- ? [value.longitude, value.latitude] : null
- /** Resolve only current identity evidence; pipeline location is the along-line geometric midpoint of its validated polyline — never an endpoint, never invented. */
- /**
- * 管线定位点 = 沿真实折线的**长度中点**(维护者 2026-10-30 15:57:不许再取起点/终点)。
- * 每段按大圆近似计长(与 pipeDiameter 同一公式),半程落在哪一段就在段内线性插值;
- * 顶点不足或全长为零(重合点)时回那个真实顶点 —— 仍是几何证据,不是编造。
- */
- function polylineMidpoint(positions) {
- const points = (Array.isArray(positions) ? positions : [])
- .filter((point) => Array.isArray(point) && Number.isFinite(point[0]) && Number.isFinite(point[1]))
- if (!points.length) return null
- if (points.length === 1) return points[0]
- const rad = Math.PI / 180
- const segmentLength = (a, b) => {
- const h = Math.sin((b[1] - a[1]) * rad / 2) ** 2
- + Math.cos(a[1] * rad) * Math.cos(b[1] * rad) * Math.sin((b[0] - a[0]) * rad / 2) ** 2
- return 12742017.6 * Math.asin(Math.sqrt(Math.min(1, h)))
- }
- const lengths = []
- let total = 0
- for (let i = 1; i < points.length; i += 1) {
- const length = segmentLength(points[i - 1], points[i])
- lengths.push(length)
- total += length
- }
- if (!(total > 0)) return points[0]
- let walked = 0
- for (let i = 0; i < lengths.length; i += 1) {
- const length = lengths[i]
- if (walked + length >= total / 2) {
- const t = length > 0 ? (total / 2 - walked) / length : 0
- return [
- points[i][0] + (points[i + 1][0] - points[i][0]) * t,
- points[i][1] + (points[i + 1][1] - points[i][1]) * t,
- ]
- }
- walked += length
- }
- return points[points.length - 1]
- }
- export function resolveFacilityPoint({ identity: input, points, detail, network } = {}) {
- const identity = facilityIdentity(input)
- if (!identity.valid) return null
- const row = (points || []).find(item => item.id === identity.id && item.type === identity.category)
- const current = detail && detail.kind === 'facility' && detail.id === identity.id && detail.category === identity.category ? detail : null
- const raw = current?.raw || {}
- let source = row || {}
- let position = null
- let placement = 'pending'
- let reasonText = ''
- let locationHint = ''
- if (identity.category === 'PIPE_NETWORK') {
- const matches = (network?.pipes || []).filter(pipe => String(pipe.sourceId ?? '') === identity.id || String(pipe.networkId ?? '') === identity.id)
- const byCode = matches.length ? matches : (network?.pipes || []).filter(pipe => raw.code && pipe.code === raw.code)
- const pipe = byCode.length === 1 ? byCode[0] : null
- if (pipe) {
- source = pipe
- position = polylineMidpoint(pipe.positions)
- placement = position ? 'drawn' : 'no-location'
- locationHint = position ? '定位到管段几何中点(沿管线长度取中点,非起止点)' : ''
- } else if (network) {
- const blocked = (network.blocked || []).find(item => raw.code && item.code === raw.code)
- placement = blocked ? 'no-location' : 'pending'
- reasonText = blocked?.reasonText || '当前地图尚未取到该管段的可定位几何'
- }
- } else if (current && Object.prototype.hasOwnProperty.call(raw, 'coordinates')) {
- position = coordinatesOf(current.coordinates)
- placement = position ? 'drawn' : 'no-location'
- reasonText = position ? '' : '设施档案未登记坐标'
- // 工程设施不是旧四类设施点契约;新定位必须有后端明确的可渲染结论。
- if (position && identity.category === 'ENGINEERING_FACILITY' && raw.coordinateValidation?.renderable !== true) {
- position = null
- placement = raw.coordinateValidation?.renderable === false ? 'no-location' : 'pending'
- reasonText = placement === 'pending' ? '设施坐标尚未获得地图可渲染校验' : (COORDINATE_REASON_TEXT[raw.coordinateValidation.reason] || NO_MAP_LOCATION_TEXT)
- }
- } else if (row) {
- position = coordinatesOf(row)
- placement = row.placement
- reasonText = row.reasonText || ''
- }
- const status = raw.status ?? row?.status ?? ''
- return {
- key: 'facility-point|' + identity.category + '|' + identity.id,
- id: identity.id, type: identity.category, detailCategory: identity.category,
- code: String(raw.code || source.code || identity.id),
- name: current?.name || source.name || String(raw.name || identity.id),
- status: String(status), tone: facilityStatusTone(status),
- industry: source.industry || raw.industry || '',
- district: source.district ?? raw.district ?? null,
- placement, longitude: position?.[0] ?? null, latitude: position?.[1] ?? null,
- reasonText, locationHint,
- }
- }
- /** Selected ledger-only entities get one temporary marker, under the SAME filters; global legend counts stay unchanged. */
- export function mergeFacilitySelectionPoints(base, selected, filters = {}) {
- if (!selected || selected.placement !== 'drawn') return base
- if (!filterFacilityPoints([selected], filters.types || [], filters).length) return base
- const list = Array.isArray(base) ? base : []
- const index = list.findIndex(row => row.key === selected.key)
- if (index < 0) return [...list, selected]
- return list.map((row, i) => i === index ? selected : row)
- }
|