import type { FeatureCollection } from 'geojson'; export interface NodeTimeSeries { demand: number[]; head: number[]; pressure: number[]; quality: number[]; } export interface LinkTimeSeries { flow: number[]; velocity: number[]; headloss: number[]; quality: number[]; energy?: number[]; efficiency?: number[]; } export interface NodeResult { id: string; type: number; coordinates: { x: number; y: number } | null; demand: number; head: number; pressure: number; quality?: number; timeSeries?: NodeTimeSeries; } export interface LinkResult { id: string; type: number; fromNode: string; toNode: string; vertices: { x: number; y: number }[]; flow: number; velocity: number; headloss: number; quality?: number; energy?: number; efficiency?: number; totalKwh?: number; peakKw?: number; avgKw?: number; utilization?: number; timeSeries?: LinkTimeSeries; } export interface SimulationResult { engine: { runtime: "native" | "wasm"; version: "2.3.5" }; timeSeconds: number; timeSteps?: number[]; units: { flow: string; pressure: string }; nodes: Record; links: Record; reportText?: string; qualityInfo?: QualityInfo; energySummary?: PumpEnergySummary; /** Warning codes (1-6) returned directly by EN_runH, independent of [REPORT] MESSAGES. */ engineWarnings?: { time: number; code: number }[]; /** [TIMES] REPORT START / REPORT TIMESTEP in seconds. timeSteps also holds * intermediate event times (tank fills, controls); use reportingStepIndices * for the periods EPANET itself reports. */ reportStart?: number; reportStep?: number; } export interface PumpEnergyResult { id: string; fromNode: string; toNode: string; totalKwh: number; peakKw: number; avgKw: number; avgEfficiency: number; utilization: number; } export interface PumpEnergySummary { pumps: Record; totalKwh: number; peakKw: number; } export interface QualityInfo { qualType: number; typeName: 'NONE' | 'CHEMICAL' | 'AGE' | 'TRACE'; chemName: string; chemUnits: string; traceNode?: string; status: 'none' | 'success' | 'failed'; errorMessage?: string; } export type ComparisonVariable = 'demand' | 'head' | 'pressure' | 'flow' | 'velocity' | 'headloss' | 'quality'; export interface ToleranceConfig { absTol: number; relTol: number; } export interface ToleranceViolation { variable: ComparisonVariable; id: string; time: number; diff: number; tolerance: number; } export interface ComparisonResult { passed: boolean; maxDiffs: Record; maxRelativeDiffs: Record; maxSignificantRelativeDiffs: Record; nearZeroCounts: Record; toleranceViolations: ToleranceViolation[]; missingIds: string[]; } export type SelectionTarget = | { type: 'node'; id: string } | { type: 'link'; id: string } | { type: 'gis-feature'; layerId: string; featureId: string | number; properties: Record; geometryType?: string; } | null; export interface MultiSelectionState { nodes: Set; links: Set; gisFeatures: Array<{ layerId: string; featureId: string | number }>; } export type EditMode = 'select' | 'add-node' | 'add-pipe'; /** Text shown next to a node / at a link midpoint (calibration values, analysis results ...). */ export interface MapAnnotation { type: 'node' | 'link'; id: string; text: string; color?: string; } /** DEM 등고선 on the map (maplibre-contour over the API's terrain tiles). */ export interface DemContours { demId: string; /** Minor contour interval (m); every 5th is a labelled major line. */ interval: number; /** Highest useful tile zoom for this DEM's resolution. */ maxzoom: number; } /** 3D 보기: tilted map, optional DEM terrain and node columns. */ export interface View3D { enabled: boolean; /** DEM id for terrain (server terrain tiles); none = flat ground. */ demId?: string; exaggeration: number; /** Node columns: height from this display value (m). */ columns: 'none' | 'head' | 'pressure' | 'elevation'; columnScale: number; } /** Image draped on the map by its four corners (DEM shading, aerial photo, scanned drawing). */ export interface RasterOverlay { id: string; name: string; url: string; /** [lng, lat] of top-left, top-right, bottom-right, bottom-left. */ corners: [number, number][]; opacity: number; visible: boolean; } /** Right dock tab. */ export type RightTab = 'inspector' | 'results' | 'verification' | 'analysis'; export type DisplayMode = | 'pressure' | 'flow' | 'head' | 'velocity' | 'headloss' | 'elevation' | 'diff-pressure' | 'diff-flow' | 'quality'; export interface Scenario { id: string; name: string; description?: string; inpStr: string; result: SimulationResult | null; createdAt: string; } export type BasemapType = | 'blank' | 'osm' | 'vworld' | 'vworld-white' | 'vworld-midnight' | 'vworld-satellite' | 'vworld-satellite-hybrid'; /* --------------------------------------------------------------------------- User GIS Reference Layers --------------------------------------------------------------------------- */ export interface UserGisLayer { id: string; name: string; fileName: string; featureCollection: FeatureCollection; featureCount: number; geomTypes: string[]; estimatedCrs: string; visible: boolean; opacity: number; // 0.0 ~ 1.0 color: string; createdAt: string; /** CRS the source coordinates were read as (EPSG code / proj4); lets the * layer be reassigned when that guess was wrong. Absent = WGS84 / unknown. */ sourceCrs?: string; } /* --------------------------------------------------------------------------- Layer Visibility State --------------------------------------------------------------------------- */ export interface LayerVisibilityState { // 관망 모델 요소별 nodes: boolean; pipes: boolean; pumps: boolean; valves: boolean; tanksReservoirs: boolean; // 레이어별 투명도 (0.0 ~ 1.0) networkOpacity: { nodes: number; pipes: number; pumps: number; valves: number; tanksReservoirs: number; }; // 결과 표시 results: { visible: boolean; opacity: number; }; } /* --------------------------------------------------------------------------- Map Context Menu Event --------------------------------------------------------------------------- */ export type ContextMenuTarget = | { type: 'canvas'; lngLat: [number, number]; modelCoords: [number, number] } | { type: 'node'; id: string; lngLat: [number, number]; modelCoords?: [number, number] } | { type: 'link'; id: string; lngLat: [number, number] } | { type: 'gis-feature'; layerId: string; featureId: string | number; properties: Record; lngLat: [number, number]; }; export interface MapContextMenuEvent { x: number; y: number; target: ContextMenuTarget; }