weather / app.py
atsuga's picture
Update app.py
ea52e23 verified
import joblib
import pandas as pd
from flask import Flask, request, jsonify, render_template
from datetime import timedelta
import os
from tensorflow.keras.models import load_model
from tensorflow.keras.preprocessing.image import load_img, img_to_array
import numpy as np
# ==============================================================================
# --- 1. KONFIGURASI JALUR & VARIABEL GLOBAL ---
# ==============================================================================
# --- A. Jalur Model Prediction Crop (Teman Anda) ---
CROP_PRED_MODEL_PATH = 'random_forest_model.pkl'
CROP_PRED_SCALER_PATH = 'scaler.pkl'
# --- B. Jalur Model Clustering (Anda) ---
MODEL_FILES_CLUSTERING = {
"bangkalan": "kmeans_model_bangkalan.joblib",
"sampang" : "kmeans_model_sampang.joblib",
"pamekasan": "kmeans_model_pamekasan.joblib",
"sumenep": "kmeans_model_sumenep.joblib",
# Tambahkan kabupaten lain di sini
}
SCALER_FILES = {
"bangkalan": "scaler_bangkalan.joblib",
"sampang": "scaler_sampang.joblib",
"pamekasan": "scaler_pamekasan.joblib",
"sumenep": "scaler_sumenep.joblib",
# Tambahkan kabupaten lain di sini
}
# --- C. Jalur Model VAR (Forecasting - Kritis) ---
# Menggunakan variabel dari kode teman Anda, namun diubah agar path tidak hardcode Windows
VAR_MODEL_PATH = "static/var_model_multivariate.joblib"
# --- D. Mapping Data CSV ---
# Ganti dengan path CSV yang AKTUAL di server Anda
CSV_MAP = {
"Bangkalan": "static/bangkalan.csv",
"Sampang": "static/sampang.csv",
"Pamekasan": "static/pamekasan.csv",
"Sumenep": "static/sumenep.csv",
}
# --- E. Variabel Global untuk Model yang Dimuat (Termasuk Model Teman Anda) ---
LOADED_MODELS_CLUSTERING = {}
LOADED_SCALERS = {}
VAR_MODEL = None # Variabel global model VAR
CROP_MODEL = None # Model Random Forest untuk crop prediction
CROP_SCALER = None # Scaler Crop Prediction
SUPPORTED_KABUPATEN_CLUSTERING = list(MODEL_FILES_CLUSTERING.keys())
FEATURE_NAMES_CROP = ['N', 'P', 'K', 'temperature', 'humidity', 'ph', 'rainfall']
# Model CUACA (Image Classification)
WEATHER_MODEL = None
WEATHER_CLASSES = [
"dew", "fogsmog", "frost", "glaze", "hail",
"lightning", "rain", "rainbow", "rime",
"sandstorm", "snow"
]
# ==============================================================================
# --- 2. FUNGSI LOADING SEMUA ASET (Berjalan saat Startup) ---
# ==============================================================================
def load_all_assets():
"""Memuat semua model (Clustering, Forecasting, Crop Prediction) ke memori."""
global LOADED_MODELS_CLUSTERING, LOADED_SCALERS, VAR_MODEL, CROP_MODEL, CROP_SCALER
print("--- MEMUAT SEMUA ASET ---")
# A. Muat Model Crop Prediction (Teman Anda)
try:
CROP_MODEL = joblib.load(CROP_PRED_MODEL_PATH)
CROP_SCALER = joblib.load(CROP_PRED_SCALER_PATH)
print(f"Model CROP ({CROP_PRED_MODEL_PATH}) dan Scaler berhasil dimuat.")
except Exception as e:
print(f"GAGAL memuat model CROP: {e}")
# B. Muat Model Clustering (Anda)
for kab, filename in MODEL_FILES_CLUSTERING.items():
try:
model_loaded = joblib.load(filename)
LOADED_MODELS_CLUSTERING[kab] = model_loaded
print(f"Model CLUSTERING {kab.title()} ({filename}) berhasil dimuat.")
except Exception as e:
print(f"GAGAL memuat model CLUSTERING {kab.title()} ({filename}): {e}")
LOADED_MODELS_CLUSTERING[kab] = None
# C. Muat Scaler Clustering (Anda)
for kab, filename in SCALER_FILES.items():
try:
scaler_loaded = joblib.load(filename)
LOADED_SCALERS[kab] = scaler_loaded
print(f"Scaler CLUSTERING {kab.title()} ({filename}) berhasil dimuat.")
except Exception as e:
print(f"GAGAL memuat scaler CLUSTERING {kab.title()} ({filename}): {e}")
LOADED_SCALERS[kab] = None
# D. Muat Model VAR/Forecasting
try:
VAR_MODEL = joblib.load(VAR_MODEL_PATH)
print(f"Model VAR ({VAR_MODEL_PATH}) berhasil dimuat.")
except Exception as e:
print(f"GAGAL memuat model VAR: {e}. PASTIKAN PATH BENAR.")
VAR_MODEL = None
# E. Muat Model Cuaca (Image Classification)
global WEATHER_MODEL
try:
WEATHER_MODEL = load_model("model_cuaca.h5")
print("Model CUACA berhasil dimuat.")
except Exception as e:
WEATHER_MODEL = None
print(f"GAGAL memuat model CUACA: {e}")
print("--- SELESAI MEMUAT SEMUA ASET ---")
# Panggil fungsi ini saat startup!
load_all_assets()
# ==============================================================================
# --- 3. INISIALISASI FLASK & ROUTE CROP PREDICTION (Teman Anda) ---
# ==============================================================================
app = Flask(__name__)
@app.route('/')
def home():
# Asumsi Anda memiliki template index.html
return render_template('index.html')
@app.route('/crop')
def crop():
# Asumsi Anda memiliki template Crop.html
return render_template('Crop.html', feature_names=FEATURE_NAMES_CROP, form_values={})
@app.route('/predict', methods=['POST'])
def predict():
# Menggunakan CROP_MODEL dan CROP_SCALER global yang sudah dimuat
if CROP_MODEL is None or CROP_SCALER is None:
return "Error: Model atau Scaler Crop Prediction tidak dimuat. Cek file .pkl Anda.", 500
try:
data = request.form.to_dict()
input_features = []
form_values = {}
for name in FEATURE_NAMES_CROP:
value = float(data[name])
input_features.append(value)
form_values[name] = value
input_df = pd.DataFrame([input_features], columns=FEATURE_NAMES_CROP)
# Scaling Data Input
features_scaled = CROP_SCALER.transform(input_df)
features_scaled = pd.DataFrame(features_scaled, columns=input_df.columns)
prediction = CROP_MODEL.predict(features_scaled)
translate = {
'rice': 'padi',
'maize': 'jagung',
'jute': 'rami',
'cotton': 'kapas',
'coconut': 'kelapa',
'papaya': 'pepaya',
'orange': 'jeruk',
'apple': 'apel',
'muskmelon': 'blewah',
'watermelon': 'semangka',
'grapes': 'anggur',
'mango': 'mangga',
'banana': 'pisang',
'pomegranate': 'delima',
'lentil': 'lentil',
'blackgram': 'kacang tunggak',
'mungbean': 'kacang hijau',
'mothbeans': 'kacang ngengat',
'pigeonpeas': 'kacang gude',
'kidneybeans': 'kacang merah',
'chickpea': 'kacang arab',
'coffee': 'kopi'
}
output = translate[prediction[0]]
# Asumsi Anda memiliki template Crop.html
return render_template('Crop.html',
prediction_text=f'{output}',
feature_names=FEATURE_NAMES_CROP,
form_values=form_values)
except KeyError as e:
# Asumsi Anda memiliki template Crop.html
return render_template('Crop.html',
error_message=f'Error: Input untuk fitur {str(e)} hilang. Pastikan semua kolom terisi.',
feature_names=FEATURE_NAMES_CROP,
form_values=request.form.to_dict()), 400
except ValueError:
# Asumsi Anda memiliki template Crop.html
return render_template('Crop.html',
error_message='Error: Semua input harus berupa angka.',
feature_names=FEATURE_NAMES_CROP,
form_values=request.form.to_dict()), 400
except Exception as e:
# Asumsi Anda memiliki template Crop.html
return render_template('Crop.html',
error_message=f'Terjadi error tak terduga: {str(e)}',
feature_names=FEATURE_NAMES_CROP,
form_values=request.form.to_dict()), 500
# ==============================================================================
# ROUTE FORECASTING PER KABUPATEN
# ==============================================================================
@app.route('/forecast/<kabupaten>')
def forecast(kabupaten):
try:
csv_map = {
"Bangkalan": r"static/bangkalan.csv",
"Sampang": r"static/sampang.csv",
"Pamekasan": r"static/pamekasan.csv",
"Sumenep": r"static/sumenep.csv"
}
model_path = r"static/var_model_multivariate.joblib"
if kabupaten not in csv_map:
return jsonify({"error": "Kabupaten tidak ditemukan"}), 404
# =====================
# BACA CSV
# =====================
df = pd.read_csv(csv_map[kabupaten])
# =====================
# SET INDEX WAKTU
# =====================
df['datetime'] = pd.to_datetime(df['datetime'])
df.set_index('datetime', inplace=True)
# =====================
# LOAD MODEL VAR
# =====================
var_model = joblib.load(model_path)
# =====================
# FEATURE ENGINEERING (SESUAI CSV ANDA)
# =====================
df['daily_temp'] = df['temp'] # dari temp
df['daily_humidity_diff'] = df['humidity'].diff() # dari humidity
df['daily_precipprob_diff'] = df['precipprob'].diff() # dari precipprob
df = df.dropna()
# =====================
# FILTER FITUR SESUAI MODEL VAR
# =====================
model_features = [
'daily_temp',
'daily_humidity_diff',
'daily_precipprob_diff'
]
df = df[model_features]
# =====================
# AMBIL 3 HARI TERAKHIR
# =====================
last_3_days = df.tail(3)
# =====================
# FORECAST 5 HARI
# =====================
lag_order = var_model.k_ar
forecast_values = var_model.forecast(
y=df.values[-lag_order:],
steps=5
)
forecast_dates = [
df.index[-1] + pd.Timedelta(days=i)
for i in range(1, 6)
]
forecast_df = pd.DataFrame(
forecast_values,
columns=df.columns,
index=forecast_dates
)
return jsonify({
"last_days": {
"dates": last_3_days.index.strftime('%Y-%m-%d').tolist(),
"values": last_3_days.values.tolist()
},
"forecast": {
"dates": forecast_df.index.strftime('%Y-%m-%d').tolist(),
"values": forecast_df.values.tolist()
},
"columns": df.columns.tolist()
})
except Exception as e:
return jsonify({"error": str(e)}), 500
@app.route('/about')
def about():
return render_template('about.html')
# ==============================================================================
# --- 5. ROUTE CLUSTERING PER KABUPATEN (Kode Anda) ---
# ==============================================================================
@app.route('/clustering/<kabupaten>')
def get_clustering_data(kabupaten):
# Logika Clustering Anda (Sudah benar dan menggunakan scaling)
try:
# PENGAMBILAN INPUT USER
tgl = int(request.args.get("tgl", 1))
bln = int(request.args.get("bln", 1))
tahun = int(request.args.get("tahun", 2024))
kabupaten_lower = kabupaten.lower()
kabupaten_title = kabupaten.title()
if kabupaten_title not in CSV_MAP:
return jsonify({"error": "Kabupaten tidak valid"}), 400
# AMBIL MODEL DAN SCALER DARI MEMORI
model_kmeans = LOADED_MODELS_CLUSTERING.get(kabupaten_lower)
scaler = LOADED_SCALERS.get(kabupaten_lower)
if model_kmeans is None:
return jsonify({"error": f"Model clustering untuk {kabupaten_title} tidak ditemukan/gagal dimuat."}), 500
if scaler is None:
return jsonify({"error": f"Scaler untuk {kabupaten_title} tidak ditemukan/gagal dimuat. Tidak dapat melakukan scaling."}), 500
# BACA CSV
try:
df = pd.read_csv(CSV_MAP[kabupaten_title])
except FileNotFoundError:
return jsonify({"error": f"Gagal memuat CSV: File data untuk {kabupaten_title} tidak ditemukan."}), 500
df["datetime"] = pd.to_datetime(df["datetime"])
# FILTER TANGGAL
selected = df[
(df["datetime"].dt.day == tgl) &
(df["datetime"].dt.month == bln) &
(df["datetime"].dt.year == tahun)
]
if selected.empty:
return jsonify({"error": f"Data cuaca untuk {tgl}/{bln}/{tahun} di {kabupaten_title} tidak ditemukan"}), 404
# Daftar 5 Fitur untuk Scaling
fitur_yang_dipakai_model = [
"tempmax",
"precipprob",
"humidity",
"solarradiation",
"windspeed"
]
# 1. Ambil data mentah (raw) dari CSV
fitur_utama_raw = selected[fitur_yang_dipakai_model].values
# 2. LAKUKAN SCALING
fitur_utama_scaled = scaler.transform(fitur_utama_raw)
# 3. PREDIKSI
cluster = int(model_kmeans.predict(fitur_utama_scaled)[0])
return jsonify({
"kabupaten": kabupaten_title,
"input_features_raw": selected[fitur_yang_dipakai_model].iloc[0].to_dict(),
"feature_names": fitur_yang_dipakai_model,
"predicted_cluster": cluster
})
except KeyError as e:
return jsonify({"error": f"Gagal memprediksi cluster: Kolom fitur hilang atau salah nama: {str(e)}. Pastikan 5 kolom fitur sudah benar."}), 500
except ValueError as e:
return jsonify({"error": f"Gagal memprediksi cluster: Kesalahan nilai input atau format data: {str(e)}."}), 500
except Exception as e:
return jsonify({"error": f"Gagal memprediksi cluster: Terjadi error tak terduga: {str(e)}"}), 500
@app.route("/cuaca", methods=["GET", "POST"])
def prediksi_cuaca():
if WEATHER_MODEL is None:
return "Error: Model CUACA tidak dimuat. Pastikan file model_cuaca.h5 tersedia di folder proyek.", 500
if request.method == "POST":
file = request.files.get("image")
if not file:
return render_template("cuaca.html", hasil=None, error="Tidak ada file yang diunggah.")
save_path = os.path.join("static", file.filename)
file.save(save_path)
# Preprocessing sesuai input model : (100x100)
img = load_img(save_path, target_size=(100, 100))
img = img_to_array(img) / 255.0
img = np.expand_dims(img, axis=0)
pred = WEATHER_MODEL.predict(img)
label_index = np.argmax(pred)
hasil = WEATHER_CLASSES[label_index]
return render_template("cuaca.html", hasil=hasil, img=save_path)
return render_template("cuaca.html", hasil=None)
# ==============================================================================
# --- BLOK RUNNING APLIKASI ---
# ==============================================================================
if __name__ == "__main__":
if __name__ == "__main__":
app.run(
host="0.0.0.0",
port=7860, # di Spaces bebas, tapi 7860 standar
debug=True
)