import os, datetime
import numpy as np
import customtkinter as ctk
from tkinter import messagebox
import matplotlib
matplotlib.use('TkAgg')
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from skyfield.api import Loader, wgs84
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
class Menu4App(ctk.CTk):
def __init__(self):
super().__init__()
self.title("Altitude Chart Analyser (Modul 4)")
self.geometry("1000x700")
ctk.set_appearance_mode("Dark")
self.load_obj = Loader(BASE_DIR, verbose=False)
self.ts = self.load_obj.timescale()
try:
self.eph = self.load_obj('de421.bsp')
except Exception:
messagebox.showerror("Error", "File de421.bsp tidak ditemukan. Silakan unduh terlebih dahulu.")
self.setup_ui()
def setup_ui(self):
self.grid_columnconfigure(0, weight=0)
self.grid_columnconfigure(1, weight=1)
self.grid_rowconfigure(0, weight=1)
self.sidebar = ctk.CTkFrame(self, width=300)
self.sidebar.grid(row=0, column=0, sticky="nsew", padx=10, pady=10)
ctk.CTkLabel(self.sidebar, text="CHART ANALYSER", font=("Segoe UI", 20, "bold"), text_color="#00E5FF").pack(pady=20)
self.entry_y = self.create_input(self.sidebar, "Tahun:", str(datetime.datetime.now().year))
self.entry_m = self.create_input(self.sidebar, "Bulan:", str(datetime.datetime.now().month))
self.entry_d = self.create_input(self.sidebar, "Tanggal:", str(datetime.datetime.now().day))
self.entry_lat = self.create_input(self.sidebar, "Latitude:", "-7.0667")
self.entry_lon = self.create_input(self.sidebar, "Longitude:", "110.4100")
self.entry_tz = self.create_input(self.sidebar, "Timezone:", "7.0")
self.entry_elev = self.create_input(self.sidebar, "Elevasi (m):", "230")
ctk.CTkButton(self.sidebar, text="▶ PROSES GRAFIK", command=self.calculate_chart).pack(pady=20, fill="x", padx=20)
self.lbl_status = ctk.CTkLabel(self.sidebar, text="Sistem Siap")
self.lbl_status.pack()
self.main_frame = ctk.CTkFrame(self)
self.main_frame.grid(row=0, column=1, sticky="nsew", padx=10, pady=10)
def create_input(self, parent, label, default):
row = ctk.CTkFrame(parent, fg_color="transparent")
row.pack(fill="x", padx=20, pady=5)
ctk.CTkLabel(row, text=label).pack(side="left")
ent = ctk.CTkEntry(row, width=80)
ent.insert(0, default)
ent.pack(side="right")
return ent
def calculate_chart(self):
self.lbl_status.configure(text="Memproses Grafik...")
for widget in self.main_frame.winfo_children(): widget.destroy()
try:
y, m, d = int(self.entry_y.get()), int(self.entry_m.get()), int(self.entry_d.get())
lat, lon, tz, elev = float(self.entry_lat.get()), float(self.entry_lon.get()), float(self.entry_tz.get()), float(self.entry_elev.get())
earth, sun, moon = self.eph['earth'], self.eph['sun'], self.eph['moon']
loc = wgs84.latlon(lat, lon, elevation_m=elev)
observer = earth + loc
hours = np.linspace(0, 24, 144)
sun_alts, moon_alts = [], []
for h in hours:
t = self.ts.utc(y, m, d, h - tz)
sun_alts.append(observer.at(t).observe(sun).apparent().altaz()[0].degrees)
moon_alts.append(observer.at(t).observe(moon).apparent().altaz()[0].degrees)
fig, ax = plt.subplots(figsize=(8, 5), facecolor='#181818')
ax.set_facecolor('#101010')
ax.plot(hours, sun_alts, color='#FFD54F', label='Matahari', linewidth=2)
ax.plot(hours, moon_alts, color='#80DEEA', label='Bulan', linewidth=2)
ax.axhline(0, color='#FF5252', linestyle='--', label='Ufuk')
ax.set_xlim(0, 24)
ax.set_xticks(np.arange(0, 25, 2))
ax.set_xticklabels([f"{int(h):02d}:00" for h in np.arange(0, 25, 2)])
ax.set_title(f"Altitude Chart: {d}/{m}/{y}", color='white')
ax.set_xlabel("Waktu Lokal (Jam)", color='white')
ax.set_ylabel("Altitude (°)", color='white')
ax.tick_params(colors='white')
ax.grid(True, color='white', linestyle=':', alpha=0.3)
ax.legend(facecolor='#181818', labelcolor='white')
canvas = FigureCanvasTkAgg(fig, master=self.main_frame)
canvas.draw()
canvas.get_tk_widget().pack(fill="both", expand=True)
self.lbl_status.configure(text="Selesai")
except Exception as e:
self.lbl_status.configure(text="Error!")
messagebox.showerror("Error", str(e))
if __name__ == "__main__":
app = Menu4App()
app.mainloop()