"use client"; /** * V2MapLegend — compact resource legend overlay rendered ON TOP of the cockpit * map card (prototype `ptica-v2/index.html`, block `.legend > .lg-block.lg-res`). * * Five rows — ЭЛЕКТРИЧЕСТВО / ВОДА / КАНАЛИЗАЦИЯ / ТЕПЛОСНАБЖЕНИЕ / ГАЗ — each * showing the distance to the nearest engineering node + a status-dot (green when * the узел is within ~1 km, orange when further). Data comes from * `analysis.utilities.summary[]` whose `subtype` values are the OSM `road_class` * tags produced by the backend (see `noise_loader._NOISE_QUERIES` + `parcels.py` * `_nearest()` aliases) — we aggregate them into the 5 prototype buckets. * * The legend is a SIBLING of `PticaMapInner` inside `V2Map` (the inner Leaflet * mount is shared with the старый ptica route and must NOT be touched). All * styles live in the scoped v2 module so the overlay never leaks into the * application chrome. */ import styles from "@/app/site-finder/analysis/[cad]/v2/v2.module.css"; import type { ParcelAnalysis } from "@/types/site-finder"; type ResourceKey = "electric" | "water" | "sewer" | "heat" | "gas"; interface ResourceDef { key: ResourceKey; label: string; /** OSM road_class values that count toward this bucket. */ subtypes: readonly string[]; /** CSS var for the dot accent color (matches prototype --res-* tokens). */ color: string; } // Aliases mirror backend `_nearest()` in `backend/app/api/v1/parcels.py`: // electric ← substation | transformer | power_line // water ← water_main | water_works | water_tower // sewer ← sewerage // heat ← heat_substation // gas ← gas_pipeline (legacy generic «pipeline» falls under gas) const RESOURCES: readonly ResourceDef[] = [ { key: "electric", label: "ЭЛЕКТРИЧЕСТВО", subtypes: ["substation", "transformer", "power_line"], color: "var(--res-electric)", }, { key: "water", label: "ВОДА", subtypes: ["water_main", "water_works", "water_tower"], color: "var(--res-water)", }, { key: "sewer", label: "КАНАЛИЗАЦИЯ", subtypes: ["sewerage"], color: "var(--res-sewer)", }, { key: "heat", label: "ТЕПЛОСНАБЖЕНИЕ", subtypes: ["heat_substation"], color: "var(--res-heat)", }, { key: "gas", label: "ГАЗ", subtypes: ["gas_pipeline", "pipeline"], color: "var(--res-gas)", }, ] as const; const OK_THRESHOLD_M = 1000; function formatDistanceMeters(m: number): string { // Prototype uses tabular-num, single-line «1 240 м». Round to whole meters. return `${Math.round(m).toLocaleString("ru-RU")} м`; } function nearestForResource( summary: ReadonlyArray<{ subtype: string; nearest_m: number }>, subtypes: readonly string[], ): number | null { let best: number | null = null; for (const u of summary) { if (!subtypes.includes(u.subtype)) continue; if (best === null || u.nearest_m < best) best = u.nearest_m; } return best; } interface Props { analysis: ParcelAnalysis; } export function V2MapLegend({ analysis }: Props) { const summary = analysis.utilities?.summary ?? []; return (