Spaces:
Sleeping
Sleeping
| import os | |
| import numpy as np | |
| import pandas as pd | |
| import gradio as gr | |
| from tensorflow.keras.models import load_model | |
| from tensorflow.keras.utils import load_img, img_to_array | |
| from math import radians, cos, sin, sqrt, atan2 | |
| import joblib | |
| import requests | |
| # Load model dan data | |
| soil_model = load_model("models/soil-type-mobilenetv2.keras") | |
| crop_model = joblib.load("models/model_random_forest.joblib") | |
| crop_model_label = joblib.load("models/label_encoder.joblib") | |
| soil_df = pd.read_csv("data/soil_data_with_class.csv") | |
| agri_df = pd.read_csv("data/tips_menanam_dan_manfaat_tanaman.csv") | |
| class_labels = ['Black Soil', 'Cinder Soil', 'Laterite Soil', 'Peat Soil', 'Yellow Soil'] | |
| OPENWEATHER_API_KEY = os.getenv("OPENWEATHER_API_KEY") | |
| def preprocess_image(img): | |
| img = img.resize((224, 224)) | |
| img_array = img_to_array(img) / 255.0 | |
| return np.expand_dims(img_array, axis=0) | |
| def predict_soil_type(img): | |
| img_array = preprocess_image(img) | |
| prediction = soil_model.predict(img_array) | |
| predicted_index = np.argmax(prediction) | |
| return class_labels[predicted_index], float(prediction[0][predicted_index]) | |
| def find_nearest_soil_data_weighted(soil_type, lat, lon, n_points=1): | |
| filtered = soil_df[soil_df['Class_Name'] == soil_type].copy() | |
| if filtered.empty: | |
| return None | |
| user_lat, user_lon = radians(lat), radians(lon) | |
| def haversine(row): | |
| lat2, lon2 = radians(row['Location_Latitude']), radians(row['Location_Longitude']) | |
| dlat = lat2 - user_lat | |
| dlon = lon2 - user_lon | |
| a = sin(dlat / 2) ** 2 + cos(user_lat) * cos(lat2) * sin(dlon / 2) ** 2 | |
| c = 2 * atan2(sqrt(a), sqrt(1 - a)) | |
| return 6371 * c | |
| filtered['Distance_km'] = filtered.apply(haversine, axis=1) | |
| nearest = filtered.nsmallest(n_points, 'Distance_km').copy() | |
| row = nearest.iloc[0] | |
| return { | |
| "latitude": float(row['Location_Latitude']), | |
| "longitude": float(row['Location_Longitude']), | |
| "pH": float(row['pH']), | |
| "N": float(row['Nitrogen_N_ppm']), | |
| "P": float(row['Phosphorus_P_ppm']), | |
| "K": float(row['Potassium_K_ppm']), | |
| "distance_km": float(row['Distance_km']) | |
| } | |
| def get_weather_data(lat, lon): | |
| if not OPENWEATHER_API_KEY: | |
| return {"temperature": 0, "humidity": 0} | |
| url = f"https://api.openweathermap.org/data/2.5/weather?lat={lat}&lon={lon}&appid={OPENWEATHER_API_KEY}&units=metric" | |
| res = requests.get(url) | |
| if res.status_code != 200: | |
| return None | |
| data = res.json() | |
| return { | |
| "temperature": float(data['main']['temp']), | |
| "humidity": float(data['main']['humidity']), | |
| } | |
| def get_farming_tips(df, crop_name): | |
| match = df[df['Nama Tanaman'].str.lower() == crop_name.lower()] | |
| if not match.empty: | |
| row = match.iloc[0] | |
| return { | |
| "Nama Tanaman": row['Nama Tanaman'], | |
| "Tips Menanam": row.get('Tips Menanam', 'Tidak tersedia'), | |
| "Manfaat": row.get('Manfaat', 'Tidak tersedia') | |
| } | |
| return {"Nama Tanaman": crop_name, "Tips Menanam": "Tidak tersedia", "Manfaat": "Tidak tersedia"} | |
| def analyze(image, lat, lon): | |
| predicted_soil, soil_accuracy = predict_soil_type(image) | |
| nearest_data = find_nearest_soil_data_weighted(predicted_soil, lat, lon) | |
| if nearest_data is None: | |
| return {"error": "Data tanah tidak ditemukan."} | |
| weather = get_weather_data(lat, lon) | |
| if not weather: | |
| return {"error": "Cuaca tidak tersedia."} | |
| input_data = pd.DataFrame([{ | |
| 'temperature': weather['temperature'], | |
| 'humidity': weather['humidity'], | |
| 'ph': nearest_data['pH'], | |
| 'N': nearest_data['N'], | |
| 'P': nearest_data['P'], | |
| 'K': nearest_data['K'], | |
| }]) | |
| if hasattr(crop_model, "predict_proba"): | |
| proba = crop_model.predict_proba(input_data)[0] | |
| top_idx = np.argsort(proba)[::-1][:5] | |
| recommended_crops = [ | |
| crop_model_label.inverse_transform([crop_model.classes_[i]])[0] | |
| for i in top_idx | |
| ] | |
| crop_percentages = [round(float(proba[i]) * 100, 2) for i in top_idx] | |
| else: | |
| pred = crop_model.predict(input_data)[0] | |
| recommended_crops = [crop_model_label.inverse_transform([pred])[0]] | |
| crop_percentages = [100.0] | |
| tips_list = [get_farming_tips(agri_df, crop) for crop in recommended_crops] | |
| return { | |
| "Jenis Tanah": predicted_soil, | |
| "Akurasi Prediksi Tanah (%)": round(soil_accuracy * 100, 2), | |
| "Cuaca": weather, | |
| "Rekomendasi Tanaman": [ | |
| {"Tanaman": crop, "Persentase (%)": percent} | |
| for crop, percent in zip(recommended_crops, crop_percentages) | |
| ], | |
| "Tips dan Manfaat": tips_list, | |
| "Data Tanah Terdekat": nearest_data | |
| } | |
| iface = gr.Interface( | |
| fn=analyze, | |
| inputs=[ | |
| gr.Image(type="pil", label="Gambar Tanah"), | |
| gr.Number(label="Latitude"), | |
| gr.Number(label="Longitude") | |
| ], | |
| outputs=gr.JSON(), | |
| title="Soil Type & Crop Recommendation", | |
| ) | |
| if __name__ == "__main__": | |
| iface.launch() | |