Continue messing with stores

This commit is contained in:
ThePetrovich 2025-06-27 19:27:19 +08:00
parent c7df38e6ce
commit 52558ed3b2
7 changed files with 148 additions and 83 deletions

View file

@ -3,8 +3,8 @@ import type { LatLngExpression } from "leaflet";
import L from "leaflet"; import L from "leaflet";
import { getCsrfToken } from "./auth"; import { getCsrfToken } from "./auth";
import type { PredictionStage, Prediction, ParsedPrediction } from "./types"; import type { PredictionStage, RawPrediction, Prediction } from "./types";
import { latestPrediction, latestPredictionParsed } from "./stores"; import { PredictionStore, RawPredictionStore, writeLocalStorage } from "./stores";
function getLatestDataset() { function getLatestDataset() {
const now = new Date(); const now = new Date();
@ -76,8 +76,10 @@ export const getForecast = async (
const data = await response.json(); const data = await response.json();
console.log("Forecast response:", data); console.log("Forecast response:", data);
latestPrediction.set(data.result); RawPredictionStore.set(data.result as RawPrediction);
latestPredictionParsed.set(parsePrediction(data.result.prediction)); PredictionStore.set(parsePrediction(data.result.prediction) as Prediction);
writeLocalStorage("rawPrediction", data.result as RawPrediction);
writeLocalStorage("prediction", parsePrediction(data.result.prediction) as Prediction);
alert("Forecast request successful!"); alert("Forecast request successful!");
// Handle the response data as needed // Handle the response data as needed
@ -87,7 +89,7 @@ export const getForecast = async (
} }
}; };
export function parsePrediction(prediction: PredictionStage[]): ParsedPrediction { export function parsePrediction(prediction: PredictionStage[]): Prediction {
const flight_path: [number, number, number][] = []; const flight_path: [number, number, number][] = [];
const launch: { latlng: LatLngExpression; datetime: Date } = {} as any; const launch: { latlng: LatLngExpression; datetime: Date } = {} as any;
const burst: { latlng: LatLngExpression; datetime: Date } = {} as any; const burst: { latlng: LatLngExpression; datetime: Date } = {} as any;

View file

@ -1,8 +1,40 @@
import { writable } from "svelte/store"; import { writable } from "svelte/store";
import type { FlightParameters, Telemetry, ParsedTelemetry } from "./types"; import type { FlightParameters, RawTelemetry, Telemetry } from "./types";
import type { Prediction, ParsedPrediction } from "./types"; import type { RawPrediction, Prediction } from "./types";
export const flightParametersStore = writable<FlightParameters>({ export const readLocalStorage = <T>(key: string, defaultValue: T): T => {
const item = localStorage.getItem(key);
if (item) {
try {
const parsed = JSON.parse(item);
if (typeof parsed === "object" && parsed !== null) {
return parsed as T;
}
} catch (error) {
console.error(`Error parsing ${key} from localStorage:`, error);
}
}
return defaultValue;
};
export const writeLocalStorage = <T>(key: string, value: T): void => {
try {
localStorage.setItem(key, JSON.stringify(value));
} catch (error) {
console.error(`Error writing ${key} to localStorage:`, error);
}
}
export const clearLocalStorage = (key: string): void => {
try {
localStorage.removeItem(key);
}
catch (error) {
console.error(`Error clearing ${key} from localStorage:`, error);
}
}
const flightParametersDefaults: FlightParameters = {
ascent_rate: 5.0, ascent_rate: 5.0,
burst_altitude: 30000.0, burst_altitude: 30000.0,
dataset: "", dataset: "",
@ -14,10 +46,24 @@ export const flightParametersStore = writable<FlightParameters>({
launch_longitude: 129.1234, launch_longitude: 129.1234,
profile: "standard_profile", profile: "standard_profile",
version: 2, version: 2,
}); };
export const latestTelemetry = writable({} as Telemetry); export const FlightParametersStore = writable<FlightParameters>(
export const latestTelemetryParsed = writable({} as ParsedTelemetry); readLocalStorage<FlightParameters>("flightParameters", flightParametersDefaults)
);
export const latestPrediction = writable({} as Prediction); export const RawTelemetryStore = writable<RawTelemetry>(
export const latestPredictionParsed = writable({} as ParsedPrediction); readLocalStorage<RawTelemetry>("rawTelemetry", {} as RawTelemetry)
);
export const TelemetryStore = writable<Telemetry>(
readLocalStorage<Telemetry>("telemetry", {} as Telemetry)
);
export const RawPredictionStore = writable<RawPrediction>(
readLocalStorage<RawPrediction>("rawPrediction", {} as RawPrediction)
);
export const PredictionStore = writable<Prediction>(
readLocalStorage<Prediction>("prediction", {} as Prediction)
);

View file

@ -1,9 +1,9 @@
import { writable } from "svelte/store" import { writable } from "svelte/store"
import L from "leaflet"; import L from "leaflet";
import type { TelemetryPoint, ParsedTelemetry } from "./types"; import type { TelemetryPoint, Telemetry } from "./types";
export function parseTelemetry(telemetry: TelemetryPoint[]): ParsedTelemetry { export function parseTelemetry(telemetry: TelemetryPoint[]): Telemetry {
const flight_path: [number, number, number][] = telemetry.map((point) => [ const flight_path: [number, number, number][] = telemetry.map((point) => [
point.latitude, point.latitude,
point.longitude, point.longitude,

View file

@ -1,4 +1,4 @@
import type { LatLngExpression } from "leaflet"; import type { LatLngExpression, LatLngLiteral } from "leaflet";
export const PROFILE_MAP = { export const PROFILE_MAP = {
Normal: "standard_profile", Normal: "standard_profile",
@ -31,7 +31,17 @@ export interface TelemetryPoint {
payload: string; payload: string;
} }
export interface ParsedTelemetry { export interface TelemetryMetadata {
complete_datetime: string;
start_datetime: string;
}
export interface RawTelemetry {
metadata: TelemetryMetadata;
telemetry: TelemetryPoint[];
}
export interface Telemetry {
flight_path: [number, number, number][]; flight_path: [number, number, number][];
launch: { launch: {
latlng: LatLngExpression; latlng: LatLngExpression;
@ -40,15 +50,6 @@ export interface ParsedTelemetry {
datapoints: TelemetryPoint[]; datapoints: TelemetryPoint[];
} }
export interface ParsedTelemetryMetadata {
complete_datetime: string;
start_datetime: string;
}
export interface Telemetry {
metadata: ParsedTelemetryMetadata;
telemetry: TelemetryPoint[];
}
export interface TrajectoryPoint { export interface TrajectoryPoint {
altitude: number; altitude: number;
@ -62,7 +63,17 @@ export interface PredictionStage {
trajectory: TrajectoryPoint[]; trajectory: TrajectoryPoint[];
} }
export interface ParsedPrediction { export interface PredictionMetadata {
complete_datetime: string;
start_datetime: string;
}
export interface RawPrediction {
metadata: PredictionMetadata;
prediction: PredictionStage[];
}
export interface Prediction {
flight_path: [number, number, number][]; flight_path: [number, number, number][];
launch: { launch: {
latlng: LatLngExpression; latlng: LatLngExpression;
@ -80,12 +91,21 @@ export interface ParsedPrediction {
flight_time: number; flight_time: number;
} }
export interface ParsedPredictionMetadata { export interface Point {
complete_datetime: string; latlng: LatLngLiteral & { alt: number };
start_datetime: string; datetime: Date;
} }
export interface Prediction { export interface PredictionData {
metadata: ParsedPredictionMetadata; launch: Point;
prediction: PredictionStage[]; landing: Point;
burst: Point;
flight_path: LatLngExpression[];
flight_time: number;
}
export interface TelemetryData {
launch: Point;
datapoints: TelemetryPoint[];
flight_path: LatLngExpression[];
} }

View file

@ -14,7 +14,7 @@
import { getForecast } from "$lib/prediction"; import { getForecast } from "$lib/prediction";
import type { FlightParameters, ProfileName } from "$lib/types"; import type { FlightParameters, ProfileName } from "$lib/types";
import { PROFILE_MAP } from "$lib/types"; import { PROFILE_MAP } from "$lib/types";
import { flightParametersStore } from '$lib/stores'; import { FlightParametersStore, writeLocalStorage } from '$lib/stores';
let isCollapsed = false; let isCollapsed = false;
let selectedProfile: ProfileName = "Normal"; let selectedProfile: ProfileName = "Normal";
@ -24,19 +24,20 @@
let startDate = now.toISOString().split("T")[0]; // YYYY-MM-DD let startDate = now.toISOString().split("T")[0]; // YYYY-MM-DD
let startTime = now.toISOString().split("T")[1].split(".")[0]; // HH:MM:SS let startTime = now.toISOString().split("T")[1].split(".")[0]; // HH:MM:SS
let inputLat = $flightParametersStore.launch_latitude.toString(); let inputLat = $FlightParametersStore.launch_latitude.toString();
let inputLng = $flightParametersStore.launch_longitude.toString(); let inputLng = $FlightParametersStore.launch_longitude.toString();
$: $flightParametersStore.profile = PROFILE_MAP[selectedProfile]; $: $FlightParametersStore.profile = PROFILE_MAP[selectedProfile];
const handleGetPrediction = async () => { const handleGetPrediction = async () => {
console.log("Fetching prediction with parameters:", $flightParametersStore); console.log("Fetching prediction with parameters:", $FlightParametersStore);
console.log(startDate, startTime); console.log(startDate, startTime);
$flightParametersStore.launch_datetime = `${startDate}T${startTime}Z`; $FlightParametersStore.launch_datetime = `${startDate}T${startTime}Z`;
writeLocalStorage<FlightParameters>("flightParameters", $FlightParametersStore);
try { try {
const response = await getForecast($flightParametersStore); const response = await getForecast($FlightParametersStore);
console.log(response); console.log(response);
// TODO: Notify other components of the new prediction. // TODO: Notify other components of the new prediction.
// const dispatch = createEventDispatcher(); // const dispatch = createEventDispatcher();
@ -52,9 +53,9 @@
const lng = parseFloat(inputLng); const lng = parseFloat(inputLng);
if (!isNaN(lat) && !isNaN(lng)) { if (!isNaN(lat) && !isNaN(lng)) {
$flightParametersStore.launch_latitude = lat; $FlightParametersStore.launch_latitude = lat;
$flightParametersStore.launch_longitude = lng; $FlightParametersStore.launch_longitude = lng;
console.log("Updated position:", $flightParametersStore.launch_latitude, $flightParametersStore.launch_longitude); console.log("Updated position:", $FlightParametersStore.launch_latitude, $FlightParametersStore.launch_longitude);
} else { } else {
console.error("Invalid coordinate input"); console.error("Invalid coordinate input");
// TODO: Show a validation error to the user. // TODO: Show a validation error to the user.
@ -67,8 +68,8 @@
* @param {number} lng The new longitude. * @param {number} lng The new longitude.
*/ */
export const updateLaunchPosition = (lat: number, lng: number) => { export const updateLaunchPosition = (lat: number, lng: number) => {
$flightParametersStore.launch_latitude = lat; $FlightParametersStore.launch_latitude = lat;
$flightParametersStore.launch_longitude = lng; $FlightParametersStore.launch_longitude = lng;
console.log("Launch position updated:", lat, lng); console.log("Launch position updated:", lat, lng);
inputLat = lat.toString(); inputLat = lat.toString();
inputLng = lng.toString(); inputLng = lng.toString();
@ -214,7 +215,7 @@
type="number" type="number"
id="startHeight" id="startHeight"
class="form-control-sm" class="form-control-sm"
bind:value={$flightParametersStore.launch_altitude} bind:value={$FlightParametersStore.launch_altitude}
/> />
</FormGroup> </FormGroup>
@ -236,7 +237,7 @@
type="number" type="number"
id="ascentRate" id="ascentRate"
class="form-control-sm" class="form-control-sm"
bind:value={$flightParametersStore.ascent_rate} bind:value={$FlightParametersStore.ascent_rate}
/> />
</FormGroup> </FormGroup>
<FormGroup class="flex-fill" spacing="mb-0"> <FormGroup class="flex-fill" spacing="mb-0">
@ -245,7 +246,7 @@
type="number" type="number"
id="descentRate" id="descentRate"
class="form-control-sm" class="form-control-sm"
bind:value={$flightParametersStore.descent_rate} bind:value={$FlightParametersStore.descent_rate}
/> />
</FormGroup> </FormGroup>
</div> </div>
@ -256,7 +257,7 @@
type="number" type="number"
id="burstAltitude" id="burstAltitude"
class="form-control-sm" class="form-control-sm"
bind:value={$flightParametersStore.burst_altitude} bind:value={$FlightParametersStore.burst_altitude}
/> />
</FormGroup> </FormGroup>

View file

@ -2,37 +2,12 @@
import { onMount, createEventDispatcher } from "svelte"; import { onMount, createEventDispatcher } from "svelte";
import * as L from "leaflet"; import * as L from "leaflet";
import type { Map as LeafletMap, LayerGroup } from "leaflet"; import type { Map as LeafletMap, LayerGroup } from "leaflet";
type LatLngExpression = [number, number] | { lat: number; lng: number };
type LatLngLiteral = { lat: number; lng: number };
import "leaflet/dist/leaflet.css"; import "leaflet/dist/leaflet.css";
import VelocityLayer from "./velocity.svelte"; import VelocityLayer from "./velocity.svelte";
import { distHaversine } from "../lib/mathutil.ts"; import { distHaversine } from "../lib/mathutil.ts";
import type { PredictionData, TelemetryData } from "../lib/types.ts";
interface Point {
latlng: LatLngLiteral & { alt: number };
datetime: Date;
}
interface PredictionData {
launch: Point;
landing: Point;
burst: Point;
flight_path: LatLngExpression[];
flight_time: number;
}
interface TelemetryPoint {
altitude: number;
datetime: string;
latitude: number;
longitude: number;
}
interface TelemetryData {
launch: Point;
datapoints: TelemetryPoint[];
flight_path: LatLngExpression[];
}
/** /**
* @type {'prediction' | 'telemetry'} * @type {'prediction' | 'telemetry'}
@ -151,6 +126,22 @@
map?.fitBounds(L.latLngBounds(telemetry.flight_path)); map?.fitBounds(L.latLngBounds(telemetry.flight_path));
}; };
export const panTo = (lat: number, lng: number) => {
if (map) {
map.setView([lat, lng], map.getZoom());
}
};
export const zoomTo = (lat: number, lng: number, zoomLevel: number) => {
if (map) {
map.setView([lat, lng], zoomLevel);
}
};
export const getMap = () => {
return map;
};
</script> </script>
<div class="map-container" bind:this={mapContainer}> <div class="map-container" bind:this={mapContainer}>

View file

@ -3,24 +3,29 @@
import ControlPanel from '../ControlPanel.svelte'; import ControlPanel from '../ControlPanel.svelte';
import Navbar from '../Navbar.svelte'; import Navbar from '../Navbar.svelte';
import { onMount } from 'svelte'; import { onMount } from 'svelte';
import { latestPredictionParsed } from '$lib/stores'; import { PredictionStore } from '$lib/stores';
import { Modal } from '@sveltestrap/sveltestrap'; import { Modal } from '@sveltestrap/sveltestrap';
import L from 'leaflet';
let map: { plotData?: (prediction: any) => void } | null = null;
let map: Map | null = null;
let panel: ControlPanel | null = null;
onMount(() => { onMount(() => {
latestPredictionParsed.subscribe((prediction) => { PredictionStore.subscribe((data) => {
if (prediction && map) { if (data) {
map.plotData?.(prediction); map?.clearMapLayers();
} }
}); });
L.DomEvent.disableClickPropagation(panel?.$element);
L.DomEvent.disableScrollPropagation(panel?.$element);
}); });
</script> </script>
<main> <main>
<Navbar /> <Navbar />
<Map bind:this={map} mode="prediction"> <Map bind:this={map} mode="prediction" bind:data={$PredictionStore}>
<ControlPanel <ControlPanel bind:this={panel} />
/>
</Map> </Map>
</main> </main>