Use extId station lookup and clamp walk durations
Add ApiClient.findStationByExtId and toStation helper; use it in useOriginStationWalk. Clamp OSRM route/step durations to a minimum walking speed (1.25 m/s) and update tests to mock the new API call.
This commit is contained in:
@@ -69,6 +69,24 @@ function normalizeHafasCoordinate(value: number | undefined): number | undefined
|
||||
return Math.abs(value) > 1000 ? value / 1e6 : value;
|
||||
}
|
||||
|
||||
type HafasLocationWithCoords = Station & {
|
||||
type: string;
|
||||
lon?: number;
|
||||
crd?: {
|
||||
x?: number;
|
||||
y?: number;
|
||||
};
|
||||
};
|
||||
|
||||
function toStation(location: HafasLocationWithCoords): Station {
|
||||
return {
|
||||
name: location.name,
|
||||
extId: location.extId,
|
||||
lat: normalizeHafasCoordinate(location.lat ?? location.crd?.y),
|
||||
lng: normalizeHafasCoordinate(location.lng ?? location.lon ?? location.crd?.x),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Client for the TimeToLeave server API. Proxy requests to HAFAS, OSRM,
|
||||
* Nominatim, and WienerLinien backends. Set `baseUrl` to your deployed app.
|
||||
@@ -226,7 +244,7 @@ export class ApiClient {
|
||||
|
||||
async searchStation(query: string): Promise<Station[]> {
|
||||
const result = await this.hafasRequest<{
|
||||
svcResL?: Array<{ res?: { match?: { locL?: Array<{ type: string; name: string; extId: string }> } } }>;
|
||||
svcResL?: Array<{ res?: { match?: { locL?: HafasLocationWithCoords[] } } }>;
|
||||
}>({
|
||||
svcReqL: [
|
||||
{
|
||||
@@ -238,7 +256,23 @@ export class ApiClient {
|
||||
const locL = result?.svcResL?.[0]?.res?.match?.locL ?? [];
|
||||
return locL
|
||||
.filter((l) => l.type === "S")
|
||||
.map((l) => ({ name: l.name, extId: l.extId }));
|
||||
.map(toStation);
|
||||
}
|
||||
|
||||
async findStationByExtId(extId: string): Promise<Station | null> {
|
||||
const result = await this.hafasRequest<{
|
||||
svcResL?: Array<{ res?: { match?: { locL?: HafasLocationWithCoords[] } } }>;
|
||||
}>({
|
||||
svcReqL: [
|
||||
{
|
||||
meth: "LocMatch",
|
||||
req: { input: { loc: { extId, type: "S" }, maxLoc: 1, field: "S" } },
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const station = result?.svcResL?.[0]?.res?.match?.locL?.find((location) => location.type === "S");
|
||||
return station ? toStation(station) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -247,17 +281,8 @@ export class ApiClient {
|
||||
* is unavailable or the base URL is empty.
|
||||
*/
|
||||
async findNearestStationByCoords(lat: number, lng: number): Promise<Station | null> {
|
||||
interface HafasLocation extends Station {
|
||||
type: string;
|
||||
lon?: number;
|
||||
crd?: {
|
||||
x?: number;
|
||||
y?: number;
|
||||
};
|
||||
}
|
||||
|
||||
const result = await this.hafasRequest<{
|
||||
svcResL?: Array<{ res?: { match?: { locL?: HafasLocation[] } } }>;
|
||||
svcResL?: Array<{ res?: { match?: { locL?: HafasLocationWithCoords[] } } }>;
|
||||
}>({
|
||||
svcReqL: [
|
||||
{
|
||||
@@ -279,15 +304,15 @@ export class ApiClient {
|
||||
],
|
||||
});
|
||||
|
||||
const stations: HafasLocation[] = result?.svcResL?.[0]?.res?.match?.locL ?? [];
|
||||
const stations: HafasLocationWithCoords[] = result?.svcResL?.[0]?.res?.match?.locL ?? [];
|
||||
if (stations.length === 0) return null;
|
||||
|
||||
// Filter to only "S" (station) type results, then pick the closest
|
||||
const stationResults = stations.filter((s: HafasLocation) => s.type === "S");
|
||||
const stationResults = stations.filter((s: HafasLocationWithCoords) => s.type === "S");
|
||||
if (stationResults.length === 0) return null;
|
||||
|
||||
// Find the closest station by Euclidean distance
|
||||
const closest = stationResults.reduce((best: HafasLocation, candidate: HafasLocation) => {
|
||||
const closest = stationResults.reduce((best: HafasLocationWithCoords, candidate: HafasLocationWithCoords) => {
|
||||
const bestLat = normalizeHafasCoordinate(best.lat ?? best.crd?.y) ?? lat;
|
||||
const bestLng = normalizeHafasCoordinate(best.lng ?? best.lon ?? best.crd?.x) ?? lng;
|
||||
const candidateLat = normalizeHafasCoordinate(candidate.lat ?? candidate.crd?.y) ?? lat;
|
||||
@@ -297,12 +322,7 @@ export class ApiClient {
|
||||
return candDist < bestDist ? candidate : best;
|
||||
}, stationResults[0]);
|
||||
|
||||
return {
|
||||
name: closest.name,
|
||||
extId: closest.extId,
|
||||
lat: normalizeHafasCoordinate(closest.lat ?? closest.crd?.y),
|
||||
lng: normalizeHafasCoordinate(closest.lng ?? closest.lon ?? closest.crd?.x),
|
||||
};
|
||||
return toStation(closest);
|
||||
}
|
||||
|
||||
async monitorStops(stopIds: string[]): Promise<WienerLinienDeparture[]> {
|
||||
|
||||
Reference in New Issue
Block a user