-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
152 install and set up zustand (#168)
* feat(ide): ✨ Add zustand and store.ts * feat(backend): ✨ Add fetch of satellite data based on name * example usage * semi-working country finder * prettier * lint * simple styling for component * feat(backend): ✨ Refactor code of zustand store to enable multiple satellite showings * small fixes * eslint and prettier
- Loading branch information
Lukas Thrane
authored and
GitHub
committed
Apr 1, 2024
1 parent
2d4e5ae
commit e51e6d8
Showing
8 changed files
with
575 additions
and
225 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
284 changes: 62 additions & 222 deletions
284
frontend/src/components/satelliteData/SatelliteDataTable.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,235 +1,75 @@ | ||
| // This component displays a table of satellite data including position and country | ||
| "use client"; | ||
| import React, { useEffect, useState } from "react"; | ||
| import { exampleData } from "../map/exampleSatData"; | ||
| import { SatelliteData, mapRawDataToSatData } from "@/lib/mapHelpers"; | ||
| import { | ||
| Table, | ||
| TableBody, | ||
| TableCaption, | ||
| TableCell, | ||
| TableHead, | ||
| TableHeader, | ||
| TableRow, | ||
| } from "@/components/ui/table"; | ||
| import * as satellite from "satellite.js"; | ||
| import { PolyUtil } from "node-geometry-library"; | ||
| import globeData from "@components/map/githubglobe/files/globe-data.json"; | ||
| import { useState, useEffect } from "react"; | ||
| import { convertSatrec, SatelliteInfo } from "@/lib/convertSatrec"; | ||
| import { useSatelliteStore } from "@/lib/store"; | ||
|
|
||
| const satellitesShown = 10; // Maximum number of satellites to display | ||
| const timeInterval = 1000; // Time interval for updating satellite positions in milliseconds | ||
| const updateInterval = 10; | ||
|
|
||
| // Extends SatelliteData with calculated position properties | ||
| interface SatelliteDataWithPosition extends SatelliteData { | ||
| latitudeDeg: string; | ||
| longitudeDeg: string; | ||
| altitude: string; | ||
| velocity: string; | ||
| } | ||
|
|
||
| export default function SatelliteDataTable() { | ||
| const [satData, setSatData] = useState<SatelliteDataWithPosition[]>([]); | ||
| export default function SatelliteDataTable({ satName }: { satName: string }) { | ||
| const { satelliteData, fetchAndSetSatelliteData } = useSatelliteStore(); | ||
| const [satelliteInfo, setSatelliteInfo] = useState<SatelliteInfo | null>( | ||
| null, | ||
| ); | ||
|
|
||
| // Fetch satellite data on component mount | ||
| useEffect(() => { | ||
| // Updates satellite positions at specified intervals | ||
| const updateSatellitePositions = () => { | ||
| const updatedData = mapRawDataToSatData(exampleData) | ||
| .slice(0, satellitesShown) | ||
| .map((data) => { | ||
| const positionAndVelocity = satellite.propagate( | ||
| data.satrec, | ||
| new Date(), | ||
| ); | ||
|
|
||
| if ( | ||
| positionAndVelocity.position && | ||
| typeof positionAndVelocity.position !== "boolean" | ||
| ) { | ||
| const gmst = satellite.gstime(new Date()); // Calculates Greenwich Mean Sidereal Time | ||
| const positionGd = satellite.eciToGeodetic( | ||
| positionAndVelocity.position, | ||
| gmst, | ||
| ); | ||
|
|
||
| // Extracts velocity from positionAndVelocity if it is not false | ||
| var velocityEci = positionAndVelocity.velocity; | ||
|
|
||
| if (typeof velocityEci !== "boolean") { | ||
| // Calculate the magnitude of the velocity vector if velocityEci is not false | ||
| var velocityMagnitude = Math.sqrt( | ||
| velocityEci.x * velocityEci.x + | ||
| velocityEci.y * velocityEci.y + | ||
| velocityEci.z * velocityEci.z, | ||
| ); | ||
|
|
||
| // Convert velocity from kilometers per second (km/s) to kilometers per hour (km/h) | ||
| velocityMagnitude = velocityMagnitude * 3600; | ||
| } else { | ||
| // Set velocityMagnitude to NaN if velocityEci is false | ||
| velocityMagnitude = NaN; | ||
| } | ||
|
|
||
| // Converts geodetic position to readable format | ||
| const latitudeDeg = satellite.degreesLat( | ||
| positionGd.latitude, | ||
| ); | ||
| const longitudeDeg = satellite.degreesLong( | ||
| positionGd.longitude, | ||
| ); | ||
| const altitude = positionGd.height; | ||
|
|
||
| return { | ||
| ...data, | ||
| latitudeDeg: latitudeDeg.toFixed(2), | ||
| longitudeDeg: longitudeDeg.toFixed(2), | ||
| altitude: altitude.toFixed(2), | ||
| velocity: velocityMagnitude.toFixed(0), | ||
| }; | ||
| } else { | ||
| return { | ||
| ...data, | ||
| latitudeDeg: "N/A", | ||
| longitudeDeg: "N/A", | ||
| altitude: "N/A", | ||
| velocity: "N/A", | ||
| }; | ||
| } | ||
| }); | ||
| fetchAndSetSatelliteData(satName); | ||
| }, [fetchAndSetSatelliteData, satName]); | ||
|
|
||
| setSatData(updatedData); | ||
| }; | ||
|
|
||
| // Performs an initial update and sets the interval for further updates | ||
| updateSatellitePositions(); | ||
| // Update satellite info every `updateInterval` ms | ||
| useEffect(() => { | ||
| const intervalId = setInterval(() => { | ||
| updateSatellitePositions(); | ||
| }, timeInterval); | ||
|
|
||
| // Cleans up the interval when the component unmounts | ||
| // Access satellite data by name | ||
| const satData = satelliteData[satName]; | ||
| if (satData) { | ||
| const updatedInfo = convertSatrec(satData.satrec, satData.name); | ||
| setSatelliteInfo(updatedInfo); | ||
| } | ||
| }, updateInterval); | ||
|
|
||
| // Clear interval on component unmount | ||
| return () => clearInterval(intervalId); | ||
| }, []); | ||
|
|
||
| return ( | ||
| <div className="m-10 flex w-full flex-col items-center justify-center"> | ||
| <Table className="w-1/2"> | ||
| <TableCaption>Satellite Data</TableCaption> | ||
| <TableHeader> | ||
| <TableRow> | ||
| <TableHead className="w-1/6">Satellite</TableHead> | ||
| <TableHead className="w-1/6">Latitude</TableHead> | ||
| <TableHead className="w-1/6">Longitude</TableHead> | ||
| <TableHead className="w-1/6">Altitude</TableHead> | ||
| <TableHead className="w-1/6">Velocity</TableHead> | ||
| <TableHead className="w-1/6">Country</TableHead> | ||
| </TableRow> | ||
| </TableHeader> | ||
| <TableBody> | ||
| {satData.map((data, index) => { | ||
| let country = "Ocean"; // Default to Ocean if no country is found | ||
| globeData.features.forEach((countryFeature) => { | ||
| // Checks if the satellite is within a country's bounding box to reduce the number of polygons to check | ||
| const boundingBoxPoints = [ | ||
| { | ||
| lat: countryFeature.bbox[1], | ||
| lng: countryFeature.bbox[0], | ||
| }, | ||
| { | ||
| lat: countryFeature.bbox[3], | ||
| lng: countryFeature.bbox[0], | ||
| }, | ||
| { | ||
| lat: countryFeature.bbox[3], | ||
| lng: countryFeature.bbox[2], | ||
| }, | ||
| { | ||
| lat: countryFeature.bbox[1], | ||
| lng: countryFeature.bbox[2], | ||
| }, | ||
| ]; | ||
|
|
||
| if ( | ||
| PolyUtil.containsLocation( | ||
| { | ||
| lat: Number(data.latitudeDeg), | ||
| lng: Number(data.longitudeDeg), | ||
| }, | ||
| boundingBoxPoints, | ||
| ) | ||
| ) { | ||
| // Handles polygons to accurately find the country | ||
| if (countryFeature.geometry.type == "Polygon") { | ||
| let boundingPolygon = | ||
| countryFeature.geometry.coordinates[0].map( | ||
| (coordinate) => ({ | ||
| lat: Number(coordinate[1]), | ||
| lng: Number(coordinate[0]), | ||
| }), | ||
| ); | ||
| }, [satelliteData, satName]); | ||
|
|
||
| if ( | ||
| PolyUtil.containsLocation( | ||
| { | ||
| lat: Number(data.latitudeDeg), | ||
| lng: Number(data.longitudeDeg), | ||
| }, | ||
| boundingPolygon, | ||
| ) | ||
| ) { | ||
| country = | ||
| countryFeature.properties.ADMIN; | ||
| } | ||
| } else if ( | ||
| countryFeature.geometry.type == | ||
| "MultiPolygon" | ||
| ) { | ||
| // Loop through each polygon array in the MultiPolygon | ||
| const multiPolygon = countryFeature.geometry | ||
| .coordinates as number[][][][]; | ||
| multiPolygon.forEach((polygon) => { | ||
| let boundingPolygon = polygon[0].map( | ||
| (coordinate) => ({ | ||
| lat: Number(coordinate[1]), | ||
| lng: Number(coordinate[0]), | ||
| }), | ||
| ); | ||
| // Display loading message if satellite info is not available | ||
| if (!satelliteInfo) { | ||
| return ( | ||
| <div className="m-20"> | ||
| <h1>Loading...</h1> | ||
| </div> | ||
| ); | ||
| } | ||
|
|
||
| if ( | ||
| PolyUtil.containsLocation( | ||
| { | ||
| lat: Number( | ||
| data.latitudeDeg, | ||
| ), | ||
| lng: Number( | ||
| data.longitudeDeg, | ||
| ), | ||
| }, | ||
| boundingPolygon, | ||
| ) | ||
| ) { | ||
| country = | ||
| countryFeature.properties.ADMIN; | ||
| } | ||
| }); | ||
| } | ||
| } | ||
| }); | ||
|
|
||
| return ( | ||
| <TableRow key={index}> | ||
| <TableCell>{data.name}</TableCell> | ||
| <TableCell>{data.latitudeDeg}° N</TableCell> | ||
| <TableCell>{data.longitudeDeg}° E</TableCell> | ||
| <TableCell> | ||
| {(Number(data.altitude) * 10).toFixed(0)}{" "} | ||
| moh | ||
| </TableCell> | ||
| <TableCell>{data.velocity} km/h</TableCell> | ||
| <TableCell>{country}</TableCell> | ||
| </TableRow> | ||
| ); | ||
| })} | ||
| </TableBody> | ||
| </Table> | ||
| return ( | ||
| <div className="m-5 rounded-lg bg-gray-800 p-6 text-white shadow-lg"> | ||
| <div className="mb-4 flex items-center justify-between"> | ||
| <h1 className="text-4xl font-bold">{satelliteInfo.name}</h1> | ||
| <div>{/* Include the dropdown arrow icon here */}</div> | ||
| </div> | ||
|
|
||
| <div className="grid grid-cols-2 gap-4"> | ||
| <div className="rounded bg-gray-700 p-4"> | ||
| <p className="text-xl">{satelliteInfo.velocity} km/s</p> | ||
| <p className="text-gray-400">Velocity</p> | ||
| </div> | ||
| <div className="rounded bg-gray-700 p-4"> | ||
| <p className="text-xl">{satelliteInfo.altitude} km</p> | ||
| <p className="text-gray-400">Altitude</p> | ||
| </div> | ||
| <div className="rounded bg-gray-700 p-4"> | ||
| <p className="text-xl">{satelliteInfo.latitudeDeg}° N</p> | ||
| <p className="text-gray-400">Latitude</p> | ||
| </div> | ||
| <div className="ounded bg-gray-700 p-4"> | ||
| <p className="text-xl">{satelliteInfo.longitudeDeg}° E</p> | ||
| <p className="text-gray-400">Longitude</p> | ||
| </div> | ||
| </div> | ||
|
|
||
| <div className="mt-4 rounded bg-gray-700 p-4"> | ||
| <p className="text-xl">Above {satelliteInfo.country}</p> | ||
| </div> | ||
| <div className="ml-4">{/* Include the flag icon here */}</div> | ||
| </div> | ||
| ); | ||
| } |
Oops, something went wrong.