Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
70 commits
Select commit Hold shift + click to select a range
78e4b45
feat:added cesium access token
Jan 15, 2026
3ca16a3
feat(): Add a navbar
JorisBly Jan 15, 2026
50c5df7
Merge pull request #20 from CPNV-ES/main
JorisBly Jan 15, 2026
2ef3fe1
feat(): Add NavBar to the view
JorisBly Jan 15, 2026
ec55ade
feat(): Change positioning and sizing
JorisBly Jan 15, 2026
e7a36e7
Merge branch 'develop' into feature/user_choose_loaction
JorisBly Jan 15, 2026
f7c9c75
feat(): Change default heading
JorisBly Jan 15, 2026
37287f8
Added moon asset loader. Closes #15
Jan 15, 2026
37b5735
Merge branch 'develop' into feature/test_moon_david
davidgalindo-git Jan 15, 2026
9583052
fix:"Moon asset is now only loaded not added to viewer. Closes #15"
Jan 15, 2026
cf541a1
Merge remote-tracking branch 'origin/feature/test_moon_david' into fe…
Jan 15, 2026
0128dce
fix:"erased pasted code"
Jan 15, 2026
7390241
feat:added cesium access token
Jan 15, 2026
5c5d636
Added moon asset loader. Closes #15
Jan 15, 2026
92c7db1
fix:added reactive mount check for viewer and cesium before loading a…
Jan 15, 2026
fe5bfb9
Merge remote-tracking branch 'origin/feature/test_moon_david' into fe…
Jan 15, 2026
02bdb70
Update Cesium access token in .env file
davidgalindo-git Jan 15, 2026
c758e6a
feat(): React to location changes
JorisBly Jan 15, 2026
3f66f51
Merge pull request #23 from CPNV-ES/feature/user_choose_loaction
JorisBly Jan 15, 2026
a17166a
Merge branch 'develop' into feature/test_moon_david
JorisBly Jan 15, 2026
e6a1566
Merge pull request #22 from CPNV-ES/feature/test_moon_david
JorisBly Jan 15, 2026
4887e74
chore(): Ignore .env file
JorisBly Jan 15, 2026
c670c16
fix(): Fix cached .env
JorisBly Jan 15, 2026
bea274f
chore():Add a .env.example file
JorisBly Jan 15, 2026
e8cfa02
feat(): add FastAPI backend for live flight data fetching
omegasofian2304 Jan 15, 2026
17b27aa
fix(): allow API access from local network
omegasofian2304 Jan 15, 2026
c12bd02
Merge pull request #29 from CPNV-ES/features/fetch_planes_position
JorisBly Jan 15, 2026
dfc3b2e
moon display 3d with start view to south (#24)
davidgalindo-git Jan 15, 2026
7257942
Feature/Refactoring (#34)
JorisBly Jan 16, 2026
30e6ae0
Feature/place moon (#38)
JorisBly Jan 20, 2026
ca0da6f
fix(): Fix import (#39)
JorisBly Jan 20, 2026
943f50f
Feature/moon as entities (#44)
JorisBly Jan 20, 2026
738f2a1
Features/fetch planes position (#33)
omegasofian2304 Jan 20, 2026
e213bf3
fetch moon api data (python) and moon phase widget (vue component) (#37)
davidgalindo-git Jan 21, 2026
65df978
Moon phase uses reactive lat and lng (#55)
davidgalindo-git Jan 21, 2026
70ecd57
Feature/fetch planes location (#57)
JorisBly Jan 21, 2026
d39768c
moon display on real-time position (#58)
davidgalindo-git Jan 22, 2026
abf58b3
Feature/fix plane position (#59)
JorisBly Jan 22, 2026
2350776
chore(): Remove .idea
JorisBly Jan 22, 2026
e6b7f10
chore(): .vscode
JorisBly Jan 22, 2026
aac470a
chore(): remove extension.json from .vscode
JorisBly Jan 22, 2026
fb54317
Feature/modifiy scene (#60)
omegasofian2304 Jan 22, 2026
f93de0e
chore(): Add commands for linux too
JorisBly Jan 22, 2026
c94f4c8
feat(): Add camera zoom using the mouse wheel (#66)
omegasofian2304 Jan 26, 2026
b32b16c
feat:"moon view center button" (#69)
davidgalindo-git Jan 26, 2026
fff463b
Feature/fix entities (#74)
JorisBly Jan 26, 2026
06c6d0a
fix:"moon phase debug (but only works on submit form)" (#67)
davidgalindo-git Jan 26, 2026
9997756
Feature/refactor view layout (#76)
JorisBly Jan 26, 2026
8f2b4b9
Feature/zoom fix (#81)
omegasofian2304 Jan 27, 2026
2a2d428
Feature/get actual position (#83)
JorisBly Jan 27, 2026
0f4616c
Feature/calculate plane moon (#75)
omegasofian2304 Jan 28, 2026
603339e
Feature/predict (#89)
omegasofian2304 Jan 28, 2026
c1e418a
setup email (#84)
davidgalindo-git Jan 29, 2026
87fd880
Feature/logs (#92)
omegasofian2304 Jan 29, 2026
daa0c3d
Feature/refactorig (#93)
JorisBly Jan 29, 2026
ac60c1a
Feature/calculations predict refactor (#94)
omegasofian2304 Jan 29, 2026
acde3e5
Feature/predict plane position (#90)
JorisBly Jan 29, 2026
1619dd8
Feature/moon zoomed window (#95)
davidgalindo-git Jan 29, 2026
3c58fc6
Hotfix/fix plane prediction (#96)
JorisBly Jan 29, 2026
cf91838
fix(): Comment broken try catch
JorisBly Jan 29, 2026
dcdc23b
Merge develop into main (#97) (#98)
JorisBly Jan 29, 2026
6043916
feat(): Expand the aircraft search area (#100)
omegasofian2304 Jan 29, 2026
1c6f5d6
Feature/moon zoomed window (#99)
davidgalindo-git Jan 29, 2026
e515688
fix:"uncomment moon viewer" (#101)
davidgalindo-git Jan 29, 2026
5ab342b
fix(): comment again MoonMiniViewer (#103)
JorisBly Jan 29, 2026
3d41b57
Hotfix/fix adding sample (#102)
JorisBly Jan 29, 2026
5cc1a1e
fix(): Moon prediction (#107)
omegasofian2304 Jan 30, 2026
4094db7
Feature/send notification (#104)
JorisBly Jan 30, 2026
3eeb71f
docs(): Improve the README (#106)
omegasofian2304 Jan 30, 2026
ecb202b
Feature/improve logs (#108)
omegasofian2304 Jan 30, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
VITE_CESIUM_ACCESS_TOKEN="YOUR_CESIUM_ACCESS_TOKEN"

VITE_MOON_PHASE_APP_ID="YOUR_APP_ID"
VITE_MOON_PHASE_APP_SECRET="YOUR_MOON_APP_SECRET"

OPENSKY_CLIENT_ID="YOUR_CLIENT_ID"
OPENSKY_CLIENT_SECRET="YOU_SECRET"

GMAIL_APP_PASSWORD="YOUR_GMAIL_APP_PASSWORD"
3 changes: 1 addition & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,8 @@ coverage
*.sw?

*.tsbuildinfo

.eslintcache

.env
# Cypress
/cypress/videos/
/cypress/screenshots/
Expand Down
10 changes: 0 additions & 10 deletions .idea/.gitignore

This file was deleted.

8 changes: 0 additions & 8 deletions .idea/Planium.iml

This file was deleted.

6 changes: 0 additions & 6 deletions .idea/jsLibraryMappings.xml

This file was deleted.

8 changes: 0 additions & 8 deletions .idea/modules.xml

This file was deleted.

6 changes: 0 additions & 6 deletions .idea/vcs.xml

This file was deleted.

3 changes: 0 additions & 3 deletions .vscode/extensions.json

This file was deleted.

113 changes: 113 additions & 0 deletions API/fetchFlights.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
"""
Author : Sofian Hussein
Date : 14.01.2026
Project : FastAPI Flights Backend
Desc : API data fetching script
"""
import requests
from os import path, getenv
from dotenv import load_dotenv
from fastapi import HTTPException

dotenv_path = path.join(path.dirname(__file__), '..', '.env')
load_dotenv(dotenv_path)


def get_opensky_token():
client_id = getenv("OPENSKY_CLIENT_ID")
client_secret = getenv("OPENSKY_CLIENT_SECRET")

auth_url = "https://auth.opensky-network.org/auth/realms/opensky-network/protocol/openid-connect/token"

payload = {
"grant_type": "client_credentials",
"client_id": client_id,
"client_secret": client_secret
}

try:
response = requests.post(auth_url, data=payload)
response.raise_for_status()

token_data = response.json()
return token_data.get("access_token")

except requests.exceptions.RequestException as e:
print(f"Erreur lors de l'authentification : {e}")
return None


def get_flights(user_lat, user_long):
"""
Fetch flight data from OpenSky Network API for Switzerland region

Args:
user_lat: User latitude in degrees
user_long: User longitude in degrees

Returns:
List of dictionaries containing flight data

Example:
[{'origin_country': 'Portugal', 'lat': 46.8, 'long': 7.0111, 'alt': 10.51, 'velocity': 196.84,
'heading': 231.15, 'vertical_rate': 2.28},...]

Field Descriptions:
"origin_country": country of origin of the plane
"lat": plane latitude (°) -90 to 90
"long": plane longitude (°) -180 to 180
"alt": plane altitude (m)
"velocity": plane speed (m/s)
"heading": plane heading (°) 0 to 360, true north
"vertical_rate": Vertical speed (m/s)
"""
url = "https://opensky-network.org/api/states/all"

"""
Compute the bounding box for fetching flight data from the OpenSky Network API
based on the user's location. The goal is to define a rectangular region that
covers the visible sky towards the south from the user's position.

In this example, we use Ste-Croix as a reference point,
Geneva Airport is excluded from the area to avoid having aircraft on the ground.
"""
params = {
"lamin": user_lat-0.52,
"lomin": user_long-1,
"lamax": user_lat+1,
"lomax": user_long+1
}
token = get_opensky_token()
# search for data at the URL specified with the parameters
response = requests.get(url, params=params, headers={'Authorization': 'Bearer ' + token})

if response.status_code != 200:
if response.status_code == 429:
raise HTTPException(status_code=429, detail="Item not found")
return response
# extract only the JSON of the response
data = response.json()

if not data.get("states"):
return []

# list that contains all flights
flights = []

# loop that iterates through the json
for state in data["states"]:
# creating a dictionary that stores only useful data
flight = {
"id": state[1],
"origin_country": state[2],
"lat": state[6],
"long": state[5],
"alt": state[7],
"velocity": state[9],
"heading": state[10],
"vertical_rate": state[11]
}
# add the flight to the list of aircraft
flights.append(flight)

return flights
179 changes: 179 additions & 0 deletions API/fetchMoon.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
import math

import numpy as np
import requests
from requests.adapters import HTTPAdapter
from urllib3 import Retry


def get_moon_data(start_time, stop_time, step): # src: https://ssd-api.jpl.nasa.gov/doc/horizons.html
"""
Fetch Moon's RA, DEC and phase from Horizon API

:param start_time: min time value for moon fetching (start)
:param stop_time: max time value for moon fetching (end)
:param step: time step for moon fetching (hours)
:return: dict containing list of moon's data at specific times
"""
url = 'https://ssd.jpl.nasa.gov/api/horizons.api'

API_fetch_params = {
'format': 'json',
'COMMAND': "'301'", # Moon
'OBJ_DATA': 'NO',
'MAKE_EPHEM': 'YES',
'EPHEM_TYPE': 'OBSERVER',
'CENTER': "'500@399'", # Earth's center coordinates
'START_TIME': f"'{start_time}'", # From 'YYYY-MM-DD'
'STOP_TIME': f"'{stop_time}'", # To 'YYYY-MM-DD'
'STEP_SIZE': f"'{step}'", # Step (ex.'1h')
'QUANTITIES': "'1,9'", # 1 = RA/DEC, Quantity 9 = Brightness/Phase
}

# setup retries to handle SSLError or connection drops
# src: https://urllib3.readthedocs.io/en/stable/reference/urllib3.util.html
# src: https://requests.readthedocs.io/en/latest/user/advanced/
session = requests.Session()
retries = Retry(
total=5, # if a request fails, try 5 more times
backoff_factor=1, # wait between tries to prevent overloading the system
status_forcelist=[502, 503, 504] # only retry if server returns 502, 503, 504 error status (bad gateway/service unavailable)
)
# apply for any url 'http://'
session.mount('http://', HTTPAdapter(max_retries=retries))

try:
response = requests.get(
url,
params=API_fetch_params, # pass api's custom params
timeout=15 # trigger retry if server doesn't respond in 15s
)
response.raise_for_status() # raise Exception for any error
data = response.json() # parse json response
except Exception as e:
print(e)
return []

# check if 'result' key exists
if not data.get('result'):
return []

result = data.get('result', '')
# check if data keyword 'SOE' was returned
if '$$SOE' not in result:
return []

lines = data.get('result', '').split('\n') # Split text in lines

moon_data = [] # dic with data to return

is_data_zone = False # data zone flag
for line in lines:
if '$$SOE' in line: # start of data
is_data_zone = True
continue
if '$$EOE' in line: # end of data
is_data_zone = False
break

if is_data_zone:
# ex: 2026-Jan-15 10:00 16 59 59.11 -27 49 59.3 -7.374 6.202\n
values = []
for v in line.split(): # loop through split values
if v.strip(): # only keep if it's not an empty string
values.append(v)

# NASA column mapping for Quantities 1,9:
# values[0,1] = Date, Time
# values[2,3,4] = RA (h, m, s)
# values[5,6,7] = DEC (d, m, s)
# values[8] = APmag
# values[9] = S-br
# values[10] = Illu% (Phase)

if len(values) >= 10:
try:
# Convert RA (sexagesimal) to decimal
ra_decimal = (
float(values[2]) # h (hours)
+ float(values[3])/60 # min to h
+ float(values[4])/3600 # seconds to h
)

# Convert DEC (sexagesimal) to decimal
# handle negative DEC for decimal addition
if "-" in values[5]:
sign=-1
else:
sign=1

dec_decimal = (
abs(float(values[5])) # d (degrees) absolute value
+ float(values[6])/60 # arcminutes to d
+ float(values[7])/3600 # arcseconds to d
)

dec_decimal *= sign # apply sign

# structure return data
entry = {
'datetime': f"{values[0]} {values[1]}", # 'YYYY-MM-DD HH:SS'
'ra': round(ra_decimal, 6), # 6 Decimal Right Ascension (hours)
'dec': round(dec_decimal,6), # 6 Decimal Declination (degrees)
'phase': float(values[-1]) # Phase decimal percentage (0.00 - 100.00)
}
moon_data.append(entry)
except Exception as e:
print(f"Error: {e}")
continue
return moon_data

def get_cesium_moon_coordinates(moon_data, altitude_m):
"""
Converts RA/Dec to X, Y, Z coordinates
:param moon_data: datetime, ra, dec, phase
:param altitude_m: altitude above Earth's surface in meters
:return: dict with timestamp, X, Y, Z coordinates and moon phase
"""
# Earth radius in meters
earth_radius = 6371000
# total distance from Earth's center to Moon
r = earth_radius + altitude_m

cesium_coordinates = []

for entry in moon_data:
# convert moon coordinates to radians src: src: https://skyandtelescope.org/astronomy-resources/right-ascension-declination-celestial-coordinates/
# RA [hours] to degrees
ra_deg = entry['ra'] * 15
# RA [degrees] to radians
ra_rad = math.radians(ra_deg)
# Dec [degrees] to radians
dec_rad = math.radians(entry['dec'])

# convert spherical to cartesian src: https://mathworld.wolfram.com/SphericalCoordinates.html
# x = r * cos(dec) * cos(ra)
# y = r * cos(dec) * sin(ra)
# z = r * sin(dec)
x = r * math.cos(dec_rad) * math.cos(ra_rad)
y = r * math.cos(dec_rad) * math.sin(ra_rad)
z = r * math.sin(dec_rad)

cesium_coordinates.append({
'timestamp': entry['datetime'],
'x': round(x, 6),
'y': round(y, 6),
'z': round(z, 6),
'phase': entry['phase']
})

return cesium_coordinates

"""
# test
raw = get_moon_data('2026-01-15', '2026-01-20', '1h')
if raw:
cesium_coordinates = get_cesium_moon_coordinates(raw, 10000)
print(f"Sample Coord: {cesium_coordinates[0]}")
print(f"Total points: {len(cesium_coordinates)}")
"""
Loading