4e92e79a99
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.
81 lines
2.6 KiB
TypeScript
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];
|
|
}
|