import { writable } from "svelte/store" import type { LatLngExpression } from "leaflet"; import L from "leaflet"; import { getCsrfToken } from "./auth"; interface TrajectoryPoint { altitude: number; datetime: string; latitude: number; longitude: number; } interface PredictionStage { stage: string; trajectory: TrajectoryPoint[]; } interface ParsedPrediction { flight_path: [number, number, number][]; launch: { latlng: LatLngExpression; datetime: Date; }; burst: { latlng: LatLngExpression; datetime: Date; }; landing: { latlng: LatLngExpression; datetime: Date; }; profile: string; flight_time: number; } export const latestPrediction = writable({ metadata: { complete_datetime: "", start_datetime: "" }, prediction: [ { stage: "", trajectory: [ { altitude: 0.0, datetime: "", latitude: 0.0, longitude: 0.0 } ] } ] }); export const latestPredictionParsed = writable({} as ParsedPrediction); function getLatestDataset() { const now = new Date(); const hours = now.getUTCHours(); const minutes = now.getUTCMinutes(); const seconds = now.getUTCSeconds(); // Round down to the nearest 6-hour interval const roundedHours = Math.floor(hours / 6) * 6; const roundedDate = new Date(now); roundedDate.setUTCHours(roundedHours, 0, 0, 0); // Subtract 6 hours to account for the lag roundedDate.setUTCHours(roundedDate.getUTCHours() - 6); return roundedDate.toISOString(); } function formatLaunchDateTime(dateObj: string | Date, timeStr: string): string { // Ensure date is a Date object const date = new Date(dateObj); // Extract date components const year = date.getFullYear(); const month = String(date.getMonth() + 1).padStart(2, '0'); const day = String(date.getDate()).padStart(2, '0'); // Format time (ensure it has seconds) let formattedTime = timeStr; if (timeStr.split(':').length === 2) { formattedTime += ':00'; // Add seconds if missing } // Combine into ISO string const isoString = new Date(`${year}-${month}-${day}T${formattedTime}Z`).toISOString(); return isoString; } export const getForecast = async (flightParameters: Record, startDate: string, startTime: string): Promise => { const launch_datetime = formatLaunchDateTime(startDate, startTime); // Create request object flightParameters.dataset = getLatestDataset(); flightParameters.launch_datetime = launch_datetime; console.log("Sending request:", flightParameters); try { // Example POST request - replace with your actual API endpoint const csrfToken = await getCsrfToken(); if (!csrfToken) { throw new Error('CSRF token not found'); } const response = await fetch('http://localhost:8000/api/predictions', { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-CSRFToken': csrfToken }, body: JSON.stringify(flightParameters), credentials: 'include' }); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } const data = await response.json(); console.log("Forecast response:", data); latestPrediction.set(data.result); latestPredictionParsed.set(parsePrediction(data.result.prediction)); alert("Forecast request successful!"); // Handle the response data as needed } catch (error) { console.error("Error sending forecast request:", error); alert("Error getting forecast: " + error); } }; export function parsePrediction(prediction: PredictionStage[]): ParsedPrediction { const flight_path: [number, number, number][] = []; const launch: { latlng: LatLngExpression; datetime: Date } = {} as any; const burst: { latlng: LatLngExpression; datetime: Date } = {} as any; const landing: { latlng: LatLngExpression; datetime: Date } = {} as any; const ascent = prediction[0].trajectory; const descent = prediction[1].trajectory; // Add the ascent track to the flight path array. ascent.forEach((item) => { let lon = item.longitude; if (lon > 180.0) { lon -= 360.0; } flight_path.push([item.latitude, lon, item.altitude]); }); // Add the descent track to the flight path array. descent.forEach((item) => { let lon = item.longitude; if (lon > 180.0) { lon -= 360.0; } flight_path.push([item.latitude, lon, item.altitude]); }); // Populate the launch, burst, and landing points const launchObj = ascent[0]; let lon = launchObj.longitude; if (lon > 180.0) { lon -= 360.0; } launch.latlng = L.latLng([launchObj.latitude, lon, launchObj.altitude]); launch.datetime = new Date(launchObj.datetime); const burstObj = descent[0]; lon = burstObj.longitude; if (lon > 180.0) { lon -= 360.0; } burst.latlng = L.latLng([burstObj.latitude, lon, burstObj.altitude]); burst.datetime = new Date(burstObj.datetime); const landingObj = descent[descent.length - 1]; lon = landingObj.longitude; if (lon > 180.0) { lon -= 360.0; } landing.latlng = L.latLng([landingObj.latitude, lon, landingObj.altitude]); landing.datetime = new Date(landingObj.datetime); const profile = prediction[1].stage === "descent" ? "standard_profile" : "float_profile"; const flight_time = (new Date(landing.datetime).getTime() - new Date(launch.datetime).getTime()) / 1000; return { flight_path, launch, burst, landing, profile, flight_time, }; }