Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 | import type { AppConfig } from "../config.js";
/** A single [lon, lat] pair (GeoJSON order). */
export type Coord = [number, number];
export interface TrackGeometry {
/** Simplified [lon, lat] polyline (route silhouette). */
simplified: Coord[];
/** Resampled elevation (m) for a sparkline, or null if geo-service omitted it. */
profile: number[] | null;
}
/**
* Best-effort: ask geo-service to analyze a GPX track. Returns the simplified
* polyline + elevation profile, or null if geo-service is unavailable / errored
* / slow — the upload must never fail because of this.
*/
export async function fetchTrackGeometry(
buffer: Buffer,
config: AppConfig,
): Promise<TrackGeometry | null> {
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), config.geoTimeoutMs);
try {
const response = await fetch(`${config.geoServiceUrl}/v1/geo/analyze`, {
method: "POST",
headers: {
"x-service-token": config.serviceToken,
"content-type": "application/gpx+xml",
},
body: new Uint8Array(buffer),
signal: controller.signal,
});
if (!response.ok) {
return null;
}
const payload = (await response.json()) as { simplified?: unknown; profile?: unknown };
if (!isCoordArray(payload.simplified)) {
return null;
}
return {
simplified: payload.simplified,
profile: isNumberArray(payload.profile) ? payload.profile : null,
};
} catch {
return null;
} finally {
clearTimeout(timeout);
}
}
function isCoordArray(value: unknown): value is Coord[] {
return (
Array.isArray(value) &&
value.every(
(p) =>
Array.isArray(p) && p.length === 2 && typeof p[0] === "number" && typeof p[1] === "number",
)
);
}
function isNumberArray(value: unknown): value is number[] {
return Array.isArray(value) && value.length > 0 && value.every((n) => typeof n === "number");
}
|