Maulidaaa commited on
Commit
0def9a2
·
1 Parent(s): 0ef6d1b

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +206 -206
app.py CHANGED
@@ -1,206 +1,206 @@
1
- import os
2
- import numpy as np
3
- import pandas as pd
4
- from flask import Flask, request, jsonify
5
- from tensorflow.keras.models import load_model
6
- from tensorflow.keras.utils import load_img, img_to_array
7
- from math import radians, cos, sin, sqrt, atan2
8
- import joblib
9
- import requests
10
- from dotenv import load_dotenv
11
- from werkzeug.utils import secure_filename
12
-
13
- # Load environment variables
14
- load_dotenv()
15
- OPENWEATHER_API_KEY = os.getenv("OPENWEATHER_API_KEY")
16
-
17
- # Inisialisasi Flask
18
- app = Flask(__name__)
19
- UPLOAD_FOLDER = 'uploads'
20
- os.makedirs(UPLOAD_FOLDER, exist_ok=True)
21
-
22
- # Load model dan data
23
- soil_model = load_model("models/soil_type_classifier.h5")
24
- crop_model = joblib.load("models/model_random_forest.joblib")
25
- crop_model_label = joblib.load("models/label_encoder.joblib")
26
- soil_df = pd.read_csv("data/soil_data_with_class.csv")
27
- agri_df = pd.read_csv("data/tips_menanam_dan_manfaat_tanaman.csv")
28
- class_labels = ['Black Soil', 'Cinder Soil', 'Laterite Soil', 'Peat Soil', 'Yellow Soil']
29
-
30
- # Fungsi preprocessing gambar
31
- def preprocess_image(img_path):
32
- img = load_img(img_path, target_size=(224, 224))
33
- img_array = img_to_array(img) / 255.0
34
- return np.expand_dims(img_array, axis=0)
35
-
36
- # Fungsi prediksi jenis tanah
37
- def predict_soil_type(img_path):
38
- img_array = preprocess_image(img_path)
39
- prediction = soil_model.predict(img_array)
40
- predicted_index = np.argmax(prediction)
41
- predicted_class = class_labels[predicted_index]
42
- accuracy = float(prediction[0][predicted_index])
43
- return predicted_class, accuracy
44
-
45
- # Fungsi hitung jarak terdekat
46
- def find_nearest_soil_data_weighted(soil_type, lat, lon, n_points=1):
47
- filtered = soil_df[soil_df['Class_Name'] == soil_type].copy()
48
- if filtered.empty:
49
- return None
50
-
51
- user_lat, user_lon = radians(lat), radians(lon)
52
-
53
- def haversine(row):
54
- lat2, lon2 = radians(row['Location_Latitude']), radians(row['Location_Longitude'])
55
- dlat = lat2 - user_lat
56
- dlon = lon2 - user_lon
57
- a = sin(dlat / 2) ** 2 + cos(user_lat) * cos(lat2) * sin(dlon / 2) ** 2
58
- c = 2 * atan2(sqrt(a), sqrt(1 - a))
59
- return 6371 * c
60
-
61
- filtered['Distance_km'] = filtered.apply(haversine, axis=1)
62
- nearest = filtered.nsmallest(n_points, 'Distance_km').copy()
63
-
64
- row = nearest.iloc[0]
65
- return {
66
- "latitude": float(row['Location_Latitude']),
67
- "longitude": float(row['Location_Longitude']),
68
- "pH": float(row['pH']),
69
- "N": float(row['Nitrogen_N_ppm']),
70
- "P": float(row['Phosphorus_P_ppm']),
71
- "K": float(row['Potassium_K_ppm']),
72
- "distance_km": float(row['Distance_km'])
73
- }
74
-
75
- # Ambil data cuaca
76
- def get_weather_data(lat, lon):
77
- url = f"https://api.openweathermap.org/data/2.5/weather?lat={lat}&lon={lon}&appid={OPENWEATHER_API_KEY}&units=metric"
78
- res = requests.get(url)
79
- if res.status_code != 200:
80
- return None
81
- data = res.json()
82
- return {
83
- "temperature": float(data['main']['temp']),
84
- "humidity": float(data['main']['humidity']),
85
- }
86
-
87
- # Fungsi untuk mengambil tips bertani dan manfaat dari agri_df
88
- def get_farming_tips(df, crop_name):
89
- crop_name_str = str(crop_name)
90
- match = df[df['Nama Tanaman'].str.lower() == crop_name_str.lower()]
91
- if not match.empty:
92
- row = match.iloc[0]
93
- return {
94
- "id": int(row['ID']) if 'ID' in row else None,
95
- "Nama Tanaman": row['Nama Tanaman'],
96
- "Tips Menanam": row.get('Tips Menanam', 'Tidak tersedia'),
97
- "Manfaat": row.get('Manfaat', 'Tidak tersedia')
98
- }
99
- return None
100
-
101
- def get_location_name(lat, lon):
102
- """
103
- Mengambil nama lokasi (reverse geocoding) dari OpenStreetMap Nominatim.
104
- """
105
- try:
106
- url = f"https://nominatim.openstreetmap.org/reverse?lat={lat}&lon={lon}&format=json"
107
- headers = {"User-Agent": "soil-api/1.0"}
108
- response = requests.get(url, headers=headers, timeout=10)
109
- if response.status_code == 200:
110
- data = response.json()
111
- return data.get("display_name", "Tidak ditemukan")
112
- else:
113
- return "Tidak ditemukan"
114
- except Exception:
115
- return "Tidak ditemukan"
116
-
117
- # Endpoint utama
118
- @app.route('/analyze', methods=['POST'])
119
- def analyze():
120
- if 'image' not in request.files:
121
- return jsonify({"error": "Gambar tidak ditemukan"}), 400
122
-
123
- image_file = request.files['image']
124
- if image_file.filename == '':
125
- return jsonify({"error": "Nama file gambar kosong"}), 400
126
-
127
- try:
128
- lat = float(request.form.get('lat'))
129
- lon = float(request.form.get('lon'))
130
- if not (-90 <= lat <= 90 and -180 <= lon <= 180):
131
- raise ValueError("Out of bounds")
132
- except:
133
- return jsonify({"error": "Koordinat tidak valid"}), 400
134
-
135
- filename = secure_filename(image_file.filename)
136
- image_path = os.path.join(UPLOAD_FOLDER, filename)
137
- image_file.save(image_path)
138
-
139
- predicted_soil, soil_accuracy = predict_soil_type(image_path)
140
- nearest_data = find_nearest_soil_data_weighted(predicted_soil, lat, lon)
141
- if nearest_data is None:
142
- return jsonify({"error": "Data jenis tanah tidak ditemukan"}), 404
143
-
144
- # Ambil nama lokasi dari OSM
145
- location_name = get_location_name(nearest_data['latitude'], nearest_data['longitude'])
146
-
147
- weather = get_weather_data(lat, lon)
148
- if not weather:
149
- return jsonify({"error": "Gagal mengambil data cuaca"}), 500
150
-
151
- input_data = pd.DataFrame([{
152
- 'temperature': weather['temperature'],
153
- 'humidity': weather['humidity'],
154
- 'ph': nearest_data['pH'],
155
- 'N': nearest_data['N'],
156
- 'P': nearest_data['P'],
157
- 'K': nearest_data['K'],
158
- }])
159
-
160
- if hasattr(crop_model, "predict_proba"):
161
- proba = crop_model.predict_proba(input_data)[0]
162
- top_idx = np.argsort(proba)[::-1][:5]
163
- recommended_crops = [
164
- str(crop_model_label.inverse_transform([crop_model.classes_[i]])[0])
165
- if hasattr(crop_model_label, "inverse_transform") else str(crop_model.classes_[i])
166
- for i in top_idx
167
- ]
168
- crop_percentages = [round(float(proba[i]) * 100, 2) for i in top_idx]
169
- else:
170
- pred = crop_model.predict(input_data)[0]
171
- recommended_crops = [str(crop_model_label.inverse_transform([pred])[0])] if hasattr(crop_model_label, "inverse_transform") else [str(pred)]
172
- crop_percentages = [100.0]
173
-
174
- tips_list = []
175
- for crop in recommended_crops:
176
- tips = get_farming_tips(agri_df, crop)
177
- tips_list.append(tips if tips else {"Tanaman": crop, "Pesan": "Data tidak tersedia"})
178
-
179
- result = {
180
- "Class_Name": predicted_soil,
181
- "soil_prediction_accuracy": round(soil_accuracy * 100, 2),
182
- "nearest_soil_data": {
183
- "latitude": nearest_data['latitude'],
184
- "longitude": nearest_data['longitude'],
185
- "location_name": location_name, # Tambahkan nama lokasi di sini
186
- "pH": nearest_data['pH'],
187
- "N": nearest_data['N'],
188
- "P": nearest_data['P'],
189
- "K": nearest_data['K'],
190
- "distance_km": round(nearest_data['distance_km'], 2)
191
- },
192
- "weather": weather,
193
- "recommended_crops": [
194
- {
195
- "crop": crop,
196
- "recommendation_percentage": percent
197
- }
198
- for crop, percent in zip(recommended_crops, crop_percentages)
199
- ],
200
- "farming_tips": tips_list
201
- }
202
-
203
- return jsonify(result)
204
-
205
- if __name__ == '__main__':
206
- app.run(debug=True)
 
1
+ import os
2
+ import numpy as np
3
+ import pandas as pd
4
+ from flask import Flask, request, jsonify
5
+ from tensorflow.keras.models import load_model
6
+ from tensorflow.keras.utils import load_img, img_to_array
7
+ from math import radians, cos, sin, sqrt, atan2
8
+ import joblib
9
+ import requests
10
+ from dotenv import load_dotenv
11
+ from werkzeug.utils import secure_filename
12
+
13
+ # Load environment variables
14
+ load_dotenv()
15
+ OPENWEATHER_API_KEY = os.getenv("OPENWEATHER_API_KEY")
16
+
17
+ # Inisialisasi Flask
18
+ app = Flask(__name__)
19
+ UPLOAD_FOLDER = '/tmp/uploads'
20
+ os.makedirs(UPLOAD_FOLDER, exist_ok=True)
21
+
22
+ # Load model dan data
23
+ soil_model = load_model("models/soil_type_classifier.h5")
24
+ crop_model = joblib.load("models/model_random_forest.joblib")
25
+ crop_model_label = joblib.load("models/label_encoder.joblib")
26
+ soil_df = pd.read_csv("data/soil_data_with_class.csv")
27
+ agri_df = pd.read_csv("data/tips_menanam_dan_manfaat_tanaman.csv")
28
+ class_labels = ['Black Soil', 'Cinder Soil', 'Laterite Soil', 'Peat Soil', 'Yellow Soil']
29
+
30
+ # Fungsi preprocessing gambar
31
+ def preprocess_image(img_path):
32
+ img = load_img(img_path, target_size=(224, 224))
33
+ img_array = img_to_array(img) / 255.0
34
+ return np.expand_dims(img_array, axis=0)
35
+
36
+ # Fungsi prediksi jenis tanah
37
+ def predict_soil_type(img_path):
38
+ img_array = preprocess_image(img_path)
39
+ prediction = soil_model.predict(img_array)
40
+ predicted_index = np.argmax(prediction)
41
+ predicted_class = class_labels[predicted_index]
42
+ accuracy = float(prediction[0][predicted_index])
43
+ return predicted_class, accuracy
44
+
45
+ # Fungsi hitung jarak terdekat
46
+ def find_nearest_soil_data_weighted(soil_type, lat, lon, n_points=1):
47
+ filtered = soil_df[soil_df['Class_Name'] == soil_type].copy()
48
+ if filtered.empty:
49
+ return None
50
+
51
+ user_lat, user_lon = radians(lat), radians(lon)
52
+
53
+ def haversine(row):
54
+ lat2, lon2 = radians(row['Location_Latitude']), radians(row['Location_Longitude'])
55
+ dlat = lat2 - user_lat
56
+ dlon = lon2 - user_lon
57
+ a = sin(dlat / 2) ** 2 + cos(user_lat) * cos(lat2) * sin(dlon / 2) ** 2
58
+ c = 2 * atan2(sqrt(a), sqrt(1 - a))
59
+ return 6371 * c
60
+
61
+ filtered['Distance_km'] = filtered.apply(haversine, axis=1)
62
+ nearest = filtered.nsmallest(n_points, 'Distance_km').copy()
63
+
64
+ row = nearest.iloc[0]
65
+ return {
66
+ "latitude": float(row['Location_Latitude']),
67
+ "longitude": float(row['Location_Longitude']),
68
+ "pH": float(row['pH']),
69
+ "N": float(row['Nitrogen_N_ppm']),
70
+ "P": float(row['Phosphorus_P_ppm']),
71
+ "K": float(row['Potassium_K_ppm']),
72
+ "distance_km": float(row['Distance_km'])
73
+ }
74
+
75
+ # Ambil data cuaca
76
+ def get_weather_data(lat, lon):
77
+ url = f"https://api.openweathermap.org/data/2.5/weather?lat={lat}&lon={lon}&appid={OPENWEATHER_API_KEY}&units=metric"
78
+ res = requests.get(url)
79
+ if res.status_code != 200:
80
+ return None
81
+ data = res.json()
82
+ return {
83
+ "temperature": float(data['main']['temp']),
84
+ "humidity": float(data['main']['humidity']),
85
+ }
86
+
87
+ # Fungsi untuk mengambil tips bertani dan manfaat dari agri_df
88
+ def get_farming_tips(df, crop_name):
89
+ crop_name_str = str(crop_name)
90
+ match = df[df['Nama Tanaman'].str.lower() == crop_name_str.lower()]
91
+ if not match.empty:
92
+ row = match.iloc[0]
93
+ return {
94
+ "id": int(row['ID']) if 'ID' in row else None,
95
+ "Nama Tanaman": row['Nama Tanaman'],
96
+ "Tips Menanam": row.get('Tips Menanam', 'Tidak tersedia'),
97
+ "Manfaat": row.get('Manfaat', 'Tidak tersedia')
98
+ }
99
+ return None
100
+
101
+ def get_location_name(lat, lon):
102
+ """
103
+ Mengambil nama lokasi (reverse geocoding) dari OpenStreetMap Nominatim.
104
+ """
105
+ try:
106
+ url = f"https://nominatim.openstreetmap.org/reverse?lat={lat}&lon={lon}&format=json"
107
+ headers = {"User-Agent": "soil-api/1.0"}
108
+ response = requests.get(url, headers=headers, timeout=10)
109
+ if response.status_code == 200:
110
+ data = response.json()
111
+ return data.get("display_name", "Tidak ditemukan")
112
+ else:
113
+ return "Tidak ditemukan"
114
+ except Exception:
115
+ return "Tidak ditemukan"
116
+
117
+ # Endpoint utama
118
+ @app.route('/analyze', methods=['POST'])
119
+ def analyze():
120
+ if 'image' not in request.files:
121
+ return jsonify({"error": "Gambar tidak ditemukan"}), 400
122
+
123
+ image_file = request.files['image']
124
+ if image_file.filename == '':
125
+ return jsonify({"error": "Nama file gambar kosong"}), 400
126
+
127
+ try:
128
+ lat = float(request.form.get('lat'))
129
+ lon = float(request.form.get('lon'))
130
+ if not (-90 <= lat <= 90 and -180 <= lon <= 180):
131
+ raise ValueError("Out of bounds")
132
+ except:
133
+ return jsonify({"error": "Koordinat tidak valid"}), 400
134
+
135
+ filename = secure_filename(image_file.filename)
136
+ image_path = os.path.join(UPLOAD_FOLDER, filename)
137
+ image_file.save(image_path)
138
+
139
+ predicted_soil, soil_accuracy = predict_soil_type(image_path)
140
+ nearest_data = find_nearest_soil_data_weighted(predicted_soil, lat, lon)
141
+ if nearest_data is None:
142
+ return jsonify({"error": "Data jenis tanah tidak ditemukan"}), 404
143
+
144
+ # Ambil nama lokasi dari OSM
145
+ location_name = get_location_name(nearest_data['latitude'], nearest_data['longitude'])
146
+
147
+ weather = get_weather_data(lat, lon)
148
+ if not weather:
149
+ return jsonify({"error": "Gagal mengambil data cuaca"}), 500
150
+
151
+ input_data = pd.DataFrame([{
152
+ 'temperature': weather['temperature'],
153
+ 'humidity': weather['humidity'],
154
+ 'ph': nearest_data['pH'],
155
+ 'N': nearest_data['N'],
156
+ 'P': nearest_data['P'],
157
+ 'K': nearest_data['K'],
158
+ }])
159
+
160
+ if hasattr(crop_model, "predict_proba"):
161
+ proba = crop_model.predict_proba(input_data)[0]
162
+ top_idx = np.argsort(proba)[::-1][:5]
163
+ recommended_crops = [
164
+ str(crop_model_label.inverse_transform([crop_model.classes_[i]])[0])
165
+ if hasattr(crop_model_label, "inverse_transform") else str(crop_model.classes_[i])
166
+ for i in top_idx
167
+ ]
168
+ crop_percentages = [round(float(proba[i]) * 100, 2) for i in top_idx]
169
+ else:
170
+ pred = crop_model.predict(input_data)[0]
171
+ recommended_crops = [str(crop_model_label.inverse_transform([pred])[0])] if hasattr(crop_model_label, "inverse_transform") else [str(pred)]
172
+ crop_percentages = [100.0]
173
+
174
+ tips_list = []
175
+ for crop in recommended_crops:
176
+ tips = get_farming_tips(agri_df, crop)
177
+ tips_list.append(tips if tips else {"Tanaman": crop, "Pesan": "Data tidak tersedia"})
178
+
179
+ result = {
180
+ "Class_Name": predicted_soil,
181
+ "soil_prediction_accuracy": round(soil_accuracy * 100, 2),
182
+ "nearest_soil_data": {
183
+ "latitude": nearest_data['latitude'],
184
+ "longitude": nearest_data['longitude'],
185
+ "location_name": location_name, # Tambahkan nama lokasi di sini
186
+ "pH": nearest_data['pH'],
187
+ "N": nearest_data['N'],
188
+ "P": nearest_data['P'],
189
+ "K": nearest_data['K'],
190
+ "distance_km": round(nearest_data['distance_km'], 2)
191
+ },
192
+ "weather": weather,
193
+ "recommended_crops": [
194
+ {
195
+ "crop": crop,
196
+ "recommendation_percentage": percent
197
+ }
198
+ for crop, percent in zip(recommended_crops, crop_percentages)
199
+ ],
200
+ "farming_tips": tips_list
201
+ }
202
+
203
+ return jsonify(result)
204
+
205
+ if __name__ == '__main__':
206
+ app.run(debug=True)