import PolylineUtility from './PolylineUtility' interface Waypoint { name: string; description?: string; lat: number; lon: number; altSmoothed: number; } interface Turnpoint { type: "TAKEOFF" | "SSS" | "ESS" | "TURNPOINT" | "GOAL"; radius: number; waypoint: Waypoint; } interface Start { timeGates: string[]; type: "RACE" | "ELAPSED-TIME"; direction?: "ENTER" | "EXIT"; } interface Goal { type: "CYLINDER" | "LINE"; deadline?: string; } interface Task { taskType: string; version: number; earthModel?: "WGS84" | "FAI_SPHERE"; turnpoints: Turnpoint[]; takeoff?: { timeOpen?: string; timeClose?: string; }; sss?: { type: "RACE" | "ELAPSED-TIME"; direction?: "ENTER" | "EXIT"; timeGates: string[]; }; goal?: Goal; } interface CompressedTurnpoint { z: string; n: string; d?: string; t?: number; } interface CompressedStart { g: string[]; d?: number; // obsolete field t: number; } interface CompressedGoal { d?: string; t?: number; } interface CompressedTask { taskType: string; version: number; t: CompressedTurnpoint[]; s?: CompressedStart; g?: CompressedGoal; e?: number; } export default class XcTrackParser { private static getTurnPointType(turnpoint: any, turnpoints: Array) { if (turnpoint.t === 2) { return "SSS" } else if (turnpoint.t === 3) { return "ESS" } else if (turnpoints.indexOf(turnpoint) === (turnpoints.length - 1)) { return "GOAL" } else if (turnpoints.indexOf(turnpoint) === 0) { return "TAKEOFF" } else { return "TURNPOINT" } } public static parseJson(originalJson: string): Task { const originalTask = JSON.parse(originalJson.replace('XCTSK: ', '')) const turnpoints = originalTask.t.map((turnpoint: any) => { turnpoint.z = PolylineUtility.decodeNums(turnpoint.z) return { type: XcTrackParser.getTurnPointType(turnpoint, originalTask.t), radius: turnpoint.z.radius, waypoint: { name: turnpoint.n, description: turnpoint.d, lat: turnpoint.z.lat, lon: turnpoint.z.lon, altSmoothed: turnpoint.z.alt, } } }) const convertedTask: Task = { taskType: originalTask.taskType, version: 1, earthModel: originalTask.e === 1 ? "FAI_SPHERE" : "WGS84", turnpoints, takeoff: originalTask.s ? { timeOpen: originalTask.s.g[0], timeClose: originalTask.s.g[1], } : undefined, sss: originalTask.s ? { type: originalTask.s.t === 1 ? "RACE" : "ELAPSED-TIME", timeGates: originalTask.s.g, direction: originalTask.s.d === 1 ? "ENTER" : "EXIT", } : undefined, goal: originalTask.g ? { type: originalTask.g.t === 1 ? "LINE" : "CYLINDER", deadline: originalTask.g.d, } : undefined, } return convertedTask } private static getCompressedTurnPointType(turnpointType: string): number { if (turnpointType === 'TAKEOFF') { return 1 } else if (turnpointType === 'SSS') { return 2 } else if (turnpointType === 'ESS') { return 3 } else { return undefined } } public static compressTask(task: Task): string { const compressedTurnpoints: CompressedTurnpoint[] = task.turnpoints.map(turnpoint => ({ z: PolylineUtility.encodeCompetitionTurnpoint(turnpoint.waypoint.lat, turnpoint.waypoint.lon, turnpoint.waypoint.altSmoothed, turnpoint.radius), n: turnpoint.waypoint.name, d: turnpoint.waypoint.description ? turnpoint.waypoint.description : undefined, t: XcTrackParser.getCompressedTurnPointType(turnpoint.type) })); const compressedStart: CompressedStart | undefined = task.takeoff ? { g: [task.takeoff.timeOpen], t: 1 // always RACE for takeoff } : undefined; const compressedGoal: CompressedGoal | undefined = task.goal ? { d: task.goal.deadline, t: task.goal.type === 'LINE' ? 1 : 2 // default is CYLINDER } : undefined; const compressedTask: CompressedTask = { taskType: task.taskType, version: 2, t: compressedTurnpoints, s: compressedStart, g: compressedGoal, e: task.earthModel === 'WGS84' ? 0 : 1 // 1 means FAI_SPHERE, 0 means WGS84 } return `XCTSK: ` + JSON.stringify(compressedTask) } }