Files
flights_web/src/features/flights-map/routesToPolylines.ts
T
gnezim 4e92e79a99 Wire filterRoutes, auto-fallback, and buy-ticket popups into Flights Map
routesToPolylines + intermediateCityIds now normalize airport codes to city
codes via the dictionaries so API responses resolve correctly. The page adds
effectiveConnections state + two effects for Angular-parity fallback
(retry connections=1 on empty direct-route result, then flip the UI toggle),
a filterRoutes memo feeding polylines and intermediateIds, and a popups memo
rendering departure + arrival buy-ticket popups in route mode only.
2026-04-17 11:00:40 +03:00

81 lines
2.6 KiB
TypeScript

/**
* Transform API-returned routes into polylines the map can render.
*
* Two modes, mirroring Angular `fetchAndDraw`:
* - Spider (departure only): one straight polyline from departure to each
* unique destination across all routes.
* - Route (departure + arrival): one polyline per route following its city
* sequence; direct routes solid, connecting routes dashed.
*
* Route entries may contain airport codes in addition to city codes; both
* functions normalize each code to its city code via the dictionaries before
* building polylines. This ensures polylines resolve against MapCanvas'
* city-code-keyed marker index regardless of what the API returns.
*
* `intermediateCityIds` returns marker IDs whose tooltips must be force-opened
* along multi-hop routes, matching Angular `updateIntermediateTooltip`.
*/
import type { IDictionaries } from "@/shared/dictionaries/index.js";
import { getCityCodeByAirportCode } from "@/shared/dictionaries/index.js";
import type {
IFlightRoute,
IFlightsMapFilterState,
IMapPolyline,
} from "./types.js";
export function routesToPolylines(
routes: IFlightRoute[],
filterState: Pick<IFlightsMapFilterState, "departure" | "arrival">,
dictionaries: IDictionaries,
): IMapPolyline[] {
if (routes.length === 0) return [];
const toCity = (code: string): string =>
getCityCodeByAirportCode(dictionaries, code) ?? code;
const hasDeparture = Boolean(filterState.departure);
const hasArrival = Boolean(filterState.arrival);
const isSpiderMode = hasDeparture && !hasArrival;
if (isSpiderMode) {
const fromCode = toCity(filterState.departure!);
const destCodes = new Set<string>();
for (const r of routes) {
if (r.route.length > 1) {
const dest = toCity(r.route[r.route.length - 1]!);
if (dest !== fromCode) destCodes.add(dest);
}
}
return [...destCodes].map((dest) => ({
id: `spider-${fromCode}-${dest}`,
cityIds: [fromCode, dest],
style: "direct",
}));
}
return routes.map((r, i) => {
const normalized = r.route.map(toCity);
return {
id: `route-${i}-${normalized.join("-")}`,
cityIds: normalized,
style: r.isDirect ? "direct" : "connecting",
};
});
}
export function intermediateCityIds(
routes: IFlightRoute[],
dictionaries: IDictionaries,
): string[] {
const toCity = (code: string): string =>
getCityCodeByAirportCode(dictionaries, code) ?? code;
const ids = new Set<string>();
for (const r of routes) {
if (r.route.length <= 2) continue;
for (let i = 1; i < r.route.length - 1; i++) ids.add(toCity(r.route[i]!));
}
return [...ids];
}