// map.jsx — Stylized cockpit map. SVG-based road network. Theme-aware via CSS vars. const MAP_BG = "var(--map-bg)"; const MAP_GRID = "var(--map-grid)"; const MAP_PARK = "var(--map-park)"; const MAP_PARK_STROKE = "var(--map-park-stroke)"; const MAP_RIVER = "var(--map-river)"; const MAP_ROAD_MINOR = "var(--map-road-minor)"; const MAP_ROAD_MAJOR_OUTER = "var(--map-road-major-outer)"; const MAP_ROAD_MAJOR_INNER = "var(--map-road-major-inner)"; // 嘉兴乍浦 (Zhapu Port, Jiaxing) — port city on Hangzhou Bay. // Layout: Hangzhou Bay sea fills the south (y > ~620). Port piers jutting south. // G15 Shen-Hai expressway runs roughly N–S (right side). 乍嘉苏 expressway diagonal. // Inland canals (东湖、独山港河) cross the city. Pinghu 老城 in the upper-middle. const ROADS_MAJOR = [ // G15 沈海高速 (N–S, right) "M 1020 40 L 1020 200 L 1010 380 L 1000 540 L 1000 620", // 乍嘉苏高速 (NW–SE diagonal) "M 60 120 L 240 220 L 420 320 L 600 400 L 760 480 L 880 560", // 海盐塘公路 (E–W arterial through old town) "M 60 280 L 280 280 L 520 300 L 780 290 L 1140 280", // 乍浦大道 (E–W, mid, leading to port) "M 60 460 L 280 460 L 520 470 L 800 470 L 1140 470", // 港区疏港路 (curves down to port) "M 600 470 L 600 560 L 580 620", "M 800 470 L 820 560 L 840 620", // 外环 — connector "M 240 220 L 240 460 L 260 600", "M 880 200 L 880 400 L 880 560", ]; const ROADS_MINOR = [ // city grid (north of bay) "M 80 160 L 1140 160","M 80 220 L 1140 220","M 80 360 L 1140 360","M 80 410 L 1140 410","M 80 540 L 980 540", "M 160 60 L 160 600","M 320 60 L 320 600","M 400 60 L 400 600","M 520 60 L 520 600","M 680 60 L 680 600","M 760 60 L 760 600","M 880 60 L 880 600","M 940 60 L 940 600", // port grid "M 540 540 L 540 620","M 660 540 L 660 620","M 720 540 L 720 620","M 780 540 L 780 620", ]; // 杭州湾 + 内河水系 const RIVERS = [ // 东湖塘 (E–W canal in city) "M 0 350 Q 200 340 380 360 T 760 350 Q 920 340 1240 360", // 独山港河 / pier inlet "M 460 470 L 470 540 L 480 600", ]; // 杭州湾 — fills bottom of map const SEA_PATH = "M -20 620 L 1260 620 L 1260 820 L -20 820 Z"; // 港池 — port basins (water inlets cut into land) const PORT_BASINS = [ "M 360 620 L 380 540 L 440 540 L 460 620 Z", "M 600 620 L 620 580 L 700 580 L 720 620 Z", "M 820 620 L 840 580 L 920 580 L 940 620 Z", ]; // 防波堤 / 码头 — piers extending into the sea const PIERS = [ "M 480 620 L 480 700 L 540 700 L 540 620", "M 740 620 L 740 720 L 800 720 L 800 620", "M 960 620 L 960 680 L 1020 680 L 1020 620", ]; const PARKS = [ // 九龙山 / 南湾绿地 { x: 220, y: 80, w: 90, h: 70, label: "九龙山" }, // 东湖公园 { x: 440, y: 220, w: 80, h: 50, label: "东湖" }, // 临港绿带 { x: 80, y: 500, w: 140, h: 90, label: "南湾绿带" }, ]; const POIS = [ { x: 320, y: 180, label: "总站·乍浦城区" }, { x: 600, y: 240, label: "氢能补能站·东湖" }, { x: 880, y: 320, label: "维保中心·G15" }, { x: 700, y: 540, label: "调度中心·港区" }, { x: 960, y: 540, label: "重卡停车场" }, { x: 240, y: 540, label: "化工园补能站" }, ]; // 12 vehicles around the city // src: T = TBOX 3296/2016国标, J = JT808/1078, B = both (TBOX + JT) // VEHICLES dataset comes from data/fleet.js (window.VEHICLES) — asset-management model. // Source badge — small T/JT chip const SourceBadge = ({ src, size = "sm" }) => { const items = src === "B" ? ["T","JT"] : src === "T" ? ["T"] : ["JT"]; const colors = { T: "var(--info)", JT: "var(--accent)" }; return ( {items.map(k => ( {k} ))} ); }; window.SourceBadge = SourceBadge; // recent path traces (last 30 min ghost trails) for animation feel const TRACES = [ { id: "浙F07179F", d: "M 200 260 L 240 250 L 280 260", color: "ok" }, { id: "浙F02002F", d: "M 540 260 L 580 256 L 620 260", color: "ok" }, { id: "浙F08638F", d: "M 540 460 L 560 500 L 580 540", color: "danger" }, ]; const StatusColor = { ok: "var(--ok)", warn: "var(--warn)", danger: "var(--danger)", idle: "var(--fg-3)", }; const VehiclePin = ({ v, selected, onClick, showHeading = true, animate = true }) => { const color = StatusColor[v.status]; return ( onClick && onClick(v)}> {/* heading cone */} {showHeading && v.status !== "idle" && ( )} {/* pulse ring (only for moving) */} {animate && v.status === "ok" && v.speed > 0 && ( )} {/* outer halo */} {/* core */} {selected && } ); }; const PoiMarker = ({ poi }) => ( {poi.label} ); const Compass = () => ( N ); const ScaleBar = ({ x = 60, y = 720 }) => ( 500m ); // the main rendered map const FleetMap = ({ selectedId, onSelect, vehicles, showLabels = false, showHeatmap = false, showPaths = true, highlightPath = null, // for playback view: a polyline string playbackProgress = 0, playbackPoint = null, variant = "default", // "default" | "minimal" | "satellite" }) => { const isMin = variant === "minimal"; // Default: pull from global fleet, only those with map coords const _vehicles = vehicles || (window.VEHICLES || []).filter(v => v.x != null && v.y != null); return ( {/* base */} {/* 杭州湾 — sea */} {!isMin && ( )} {/* 港池 — port water basins cut into land */} {!isMin && PORT_BASINS.map((d, i) => ( ))} {/* coastline marker */} {!isMin && ( )} {/* sea label */} {!isMin && ( 杭州湾 · 乍浦港 )} {/* parks */} {!isMin && PARKS.map((p, i) => ( {p.label && ( {p.label} )} ))} {/* river */} {!isMin && RIVERS.map((d, i) => ( ))} {/* piers */} {!isMin && PIERS.map((d, i) => ( ))} {/* heatmap layer */} {showHeatmap && _vehicles.map((v, i) => ( ))} {/* minor roads */} {ROADS_MINOR.map((d, i) => ( ))} {/* major roads casing + center stripe */} {ROADS_MAJOR.map((d, i) => ( ))} {/* vignette */} {/* highlighted path */} {highlightPath && ( )} {/* recent traces */} {showPaths && !highlightPath && TRACES.map((t, i) => ( ))} {/* POIs */} {!isMin && POIS.map((p, i) => )} {/* vehicles */} {_vehicles.map(v => ( ))} {/* selected vehicle label */} {selectedId && _vehicles.filter(v => v.id === selectedId).map(v => ( {v.id} {v.speed}km/h ))} {/* playback marker */} {playbackPoint && ( )} {/* HUD overlays */} ); }; window.FleetMap = FleetMap;