lyimo commited on
Commit
74a5f75
ยท
verified ยท
1 Parent(s): 7f1ac2f

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +167 -183
app.py CHANGED
@@ -2,252 +2,228 @@
2
 
3
  import gradio as gr
4
  import numpy as np
5
- from part1_data import TobaccoAnalyzer
6
- from part2_visualization import VisualizationHandler
7
- import ee
 
 
 
 
 
8
 
9
- def analyze_location(location_name):
10
- """Main analysis function with enhanced historical, forecast, and NDVI data"""
11
- try:
12
- # Initialize Earth Engine
 
 
 
 
 
 
 
 
13
  try:
14
- ee.Initialize()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
15
  except Exception as e:
16
- print(f"Earth Engine initialization error: {e}")
17
-
 
 
 
 
18
  analyzer = TobaccoAnalyzer()
19
  visualizer = VisualizationHandler(analyzer.optimal_conditions)
20
 
21
  # Get coordinates from location name
22
  location_data = analyzer.geocode_location(location_name)
23
  if not location_data:
24
- return None, None, "Location not found. Please try a different location name.", None, None
25
 
26
  lat, lon = location_data['lat'], location_data['lon']
27
 
28
- # Get enhanced weather data
29
  df = analyzer.get_weather_data(lat, lon, historical_days=90, forecast_days=90)
30
- if df.empty:
31
- return None, None, "Unable to fetch weather data. Please try again.", None, None
32
 
33
- # Get NDVI data
34
- ndvi_data = analyzer.get_ndvi_data(lat, lon)
35
- ndvi_score = analyzer.calculate_ndvi_score(ndvi_data) if ndvi_data else None
36
 
37
- # Calculate scores and trends as before
38
  historical = df[df['type'] == 'historical']
39
  forecast = df[df['type'].isin(['forecast_5day', 'forecast_extended'])]
40
 
41
- temp_score = np.clip((df['temperature'].mean() - 15) / (30 - 15), 0, 1)
42
- humidity_score = np.clip((df['humidity'].mean() - 50) / (80 - 50), 0, 1)
43
- rainfall_score = np.clip(df['rainfall'].mean() / 5, 0, 1)
44
-
45
- trends = analyzer.analyze_trends(df)
 
 
46
 
47
- trend_factor = 0.1
48
- temp_score += trends['historical']['temperature']['trend'] * trend_factor
49
- humidity_score += trends['historical']['humidity']['trend'] * trend_factor
50
- rainfall_score += trends['historical']['rainfall']['trend'] * trend_factor
 
 
51
 
52
- # Calculate combined score including NDVI
53
- if ndvi_score is not None:
54
- overall_score = np.average([temp_score, humidity_score, rainfall_score, ndvi_score],
55
- weights=[0.3, 0.2, 0.2, 0.3])
56
- else:
57
- overall_score = np.mean([temp_score, humidity_score, rainfall_score])
 
 
 
 
58
 
 
 
59
  overall_score = np.clip(overall_score, 0, 1)
60
 
61
- # Create enhanced visualizations
62
  time_series_plot = visualizer.create_interactive_plots(df)
63
  gauge_plot = visualizer.create_gauge_chart(overall_score)
64
- location_map = visualizer.create_enhanced_map(lat, lon, overall_score, ndvi_data)
65
-
66
- # Generate NDVI analysis text
67
- ndvi_analysis = ""
68
- if ndvi_data and 'stats' in ndvi_data:
69
- ndvi_stats = ndvi_data['stats']
70
- mean_ndvi = ndvi_stats.get('NDVI_mean', 0)
71
- ndvi_analysis = f"""
72
- ๐ŸŒฟ NDVI Analysis:
73
- Mean NDVI: {mean_ndvi:.3f}
74
- Vegetation Health: {get_ndvi_status(mean_ndvi)}
75
- NDVI Score: {ndvi_score:.2f}
76
- Contribution to Overall Score: 30%
77
- """
78
-
79
- # Generate recommendation and analysis text
80
- recommendation = get_recommendation(overall_score)
81
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
82
  analysis_text = f"""
83
  ๐Ÿ“ Location Analysis:
84
- Location: {location_data['address']}
85
  Coordinates: {lat:.4f}ยฐN, {lon:.4f}ยฐE
86
 
87
  ๐ŸŒก๏ธ Historical Weather Analysis (Past 90 Days):
88
- Temperature: {historical['temperature'].mean():.1f}ยฐC (ยฑ{historical['temperature'].std():.1f}ยฐC)
89
- Humidity: {historical['humidity'].mean():.1f}% (ยฑ{historical['humidity'].std():.1f}%)
90
- Rainfall: {historical['rainfall'].mean():.1f}mm/day (ยฑ{historical['rainfall'].std():.1f}mm)
91
 
92
  ๐Ÿ“ˆ Weather Trends:
93
- Temperature Trend: {trends['historical']['temperature']['trend']:.3f}ยฐC/day
94
- Humidity Trend: {trends['historical']['humidity']['trend']:.3f}%/day
95
- Rainfall Trend: {trends['historical']['rainfall']['trend']:.3f}mm/day
96
 
97
- ๐Ÿ”ฎ Forecast Analysis (Next 90 Days):
98
- Temperature: {forecast['temperature'].mean():.1f}ยฐC (ยฑ{forecast['temperature'].std():.1f}ยฐC)
99
- Humidity: {forecast['humidity'].mean():.1f}% (ยฑ{forecast['humidity'].std():.1f}%)
100
- Rainfall: {forecast['rainfall'].mean():.1f}mm/day (ยฑ{forecast['rainfall'].std():.1f}mm)
 
 
101
 
102
- {ndvi_analysis}
 
 
 
 
 
 
103
 
104
- ๐Ÿ“Š Condition Scores:
 
105
  Temperature Score: {temp_score:.2f}
106
  Humidity Score: {humidity_score:.2f}
107
  Rainfall Score: {rainfall_score:.2f}
108
- NDVI Score: {ndvi_score:.2f if ndvi_score is not None else 'N/A'}
109
  Overall Score: {overall_score:.2f}
110
 
111
- ๐ŸŽฏ {recommendation}
112
-
113
- ๐Ÿ’ฐ Credit Risk Assessment:
114
- Risk Level: {get_risk_level(overall_score)}
115
- Recommended Actions: {get_risk_recommendations(overall_score)}
116
  """
117
 
118
- return time_series_plot, gauge_plot, analysis_text, location_map, gr.update(visible=False)
119
 
120
  except Exception as e:
121
  error_message = f"An error occurred: {str(e)}"
122
- return None, None, error_message, None, gr.update(visible=True, value=error_message)
123
-
124
- def get_ndvi_status(ndvi):
125
- """Get vegetation status based on NDVI value"""
126
- if ndvi < 0:
127
- return "Very Poor - Bare soil or water"
128
- elif ndvi < 0.2:
129
- return "Poor - Sparse or stressed vegetation"
130
- elif ndvi < 0.4:
131
- return "Moderate - Typical agricultural land"
132
- elif ndvi < 0.6:
133
- return "Good - Healthy vegetation"
134
- else:
135
- return "Excellent - Dense, very healthy vegetation"
136
-
137
- def get_recommendation(score):
138
- """Get detailed recommendation based on score"""
139
- if score >= 0.8:
140
- return "Excellent conditions for tobacco growing! High probability of successful crop."
141
- elif score >= 0.6:
142
- return "Good conditions. Regular monitoring recommended. Consider crop insurance."
143
- elif score >= 0.4:
144
- return "Moderate conditions. Additional interventions may be needed. Insurance strongly recommended."
145
- else:
146
- return "Poor conditions. High risk for tobacco growing. Consider alternative crops or timing."
147
 
148
- def get_risk_level(score):
149
- """Determine credit risk level based on overall score"""
150
  if score >= 0.8:
151
- return "Low Risk"
152
  elif score >= 0.6:
153
- return "Moderate Risk"
154
  elif score >= 0.4:
155
- return "High Risk"
156
- else:
157
- return "Very High Risk"
158
 
159
- def get_risk_recommendations(score):
160
- """Get credit-related recommendations based on score"""
161
  if score >= 0.8:
162
- return "Standard loan terms, consider preferential rates"
163
  elif score >= 0.6:
164
- return "Regular loan terms with mandatory crop insurance"
165
  elif score >= 0.4:
166
- return "Higher interest rates, reduced loan amount, mandatory insurance"
167
- else:
168
- return "Consider alternative financing options or different crop selection"
169
 
170
- # Create custom theme
171
- custom_theme = gr.themes.Monochrome(
172
- primary_hue="blue",
173
- secondary_hue="blue",
174
- neutral_hue="gray",
175
- radius_size=gr.themes.sizes.radius_sm,
176
- font=[gr.themes.GoogleFont("Open Sans"), "ui-sans-serif", "system-ui", "sans-serif"],
177
- )
178
-
179
- # Create Gradio interface with enhanced layout
180
- with gr.Blocks(theme=custom_theme) as iface:
181
  gr.Markdown(
182
  """
183
- # ๐ŸŒฑ Agricultural Credit Risk Assessment System
184
- ## Integrating Weather, NDVI, and Credit Scoring for Tobacco Farming
185
- """,
186
- elem_classes="custom-markdown"
187
  )
188
 
189
  with gr.Row():
190
- with gr.Column(scale=4):
191
- location_input = gr.Textbox(
192
- label="Enter Location",
193
- placeholder="e.g., Tabora, Tanzania",
194
- lines=1
195
- )
196
- with gr.Column(scale=1):
197
- analyze_button = gr.Button("๐Ÿ” Analyze", variant="primary")
198
 
199
- with gr.Tabs():
200
- with gr.TabItem("๐Ÿ“Š Analysis Dashboard"):
201
- with gr.Row():
202
- analysis_text = gr.Textbox(
203
- label="Comprehensive Analysis Results",
204
- lines=20,
205
- interactive=False
206
- )
207
-
208
- with gr.Row():
209
- weather_plot = gr.Plot(label="Weather Analysis")
210
-
211
- with gr.Row():
212
- with gr.Column(scale=1):
213
- score_gauge = gr.Plot(label="Credit Risk Score")
214
- with gr.Column(scale=1):
215
- location_map = gr.HTML(label="Location & NDVI Analysis")
216
-
217
- with gr.TabItem("โ„น๏ธ Help & Information"):
218
- gr.Markdown("""
219
- ### How to Use This Tool
220
- 1. Enter a location name in Tanzania (preferably in Tabora region)
221
- 2. Click 'Analyze' to get comprehensive results
222
- 3. View weather patterns, NDVI analysis, and credit risk assessment
223
-
224
- ### Understanding the Results
225
- - Weather Analysis: Historical and forecast data
226
- - NDVI: Vegetation health indicator
227
- - Credit Risk Score: Combined assessment of all factors
228
- - Recommendations: Specific actions based on analysis
229
-
230
- ### Important Notes
231
- - All scores are normalized to 0-1 scale
232
- - NDVI analysis requires clear satellite imagery
233
- - Weather forecasts are estimates
234
- """)
235
 
236
- # Error message container
237
- error_output = gr.Textbox(
238
- label="Status",
239
- visible=False,
240
- interactive=False
241
- )
242
 
243
- analyze_button.click(
244
- fn=analyze_location,
245
- inputs=[location_input],
246
- outputs=[weather_plot, score_gauge, analysis_text, location_map, error_output]
247
- )
248
 
249
- # Example locations in Tanzania
250
- gr.Examples(
251
  examples=[
252
  ["Tabora, Tanzania"],
253
  ["Urambo, Tabora, Tanzania"],
@@ -255,9 +231,17 @@ with gr.Blocks(theme=custom_theme) as iface:
255
  ["Nzega, Tabora, Tanzania"]
256
  ],
257
  inputs=location_input,
258
- outputs=[weather_plot, score_gauge, analysis_text, location_map, error_output],
259
  fn=analyze_location,
260
  cache_examples=True,
 
 
 
 
 
 
 
 
261
  )
262
 
263
  # Launch the app
 
2
 
3
  import gradio as gr
4
  import numpy as np
5
+ from datetime import datetime, timedelta
6
+ import folium
7
+ from folium import plugins
8
+ import requests
9
+ from geopy.geocoders import Nominatim
10
+ from geopy.exc import GeocoderTimedOut
11
+ import pandas as pd
12
+ from scipy import stats
13
 
14
+ class TobaccoAnalyzer:
15
+ def __init__(self):
16
+ self.api_key = os.getenv('OPENWEATHER_API_KEY', 'default_key')
17
+ self.optimal_conditions = {
18
+ 'temperature': {'min': 20, 'max': 30},
19
+ 'humidity': {'min': 60, 'max': 80},
20
+ 'rainfall': {'min': 500/365, 'max': 1200/365}
21
+ }
22
+ self.geolocator = Nominatim(user_agent="tobacco_analyzer")
23
+
24
+ def get_osm_data(self, lat, lon, radius=2000):
25
+ """Get land use data from OpenStreetMap"""
26
  try:
27
+ # OSM Overpass API query for land use
28
+ overpass_url = "http://overpass-api.de/api/interpreter"
29
+ query = f"""
30
+ [out:json];
31
+ (
32
+ way["landuse"](around:{radius},{lat},{lon});
33
+ relation["landuse"](around:{radius},{lat},{lon});
34
+ );
35
+ out body;
36
+ >;
37
+ out skel qt;
38
+ """
39
+ response = requests.get(overpass_url, params={'data': query})
40
+ if response.status_code == 200:
41
+ return response.json()
42
+ return None
43
  except Exception as e:
44
+ print(f"Error fetching OSM data: {e}")
45
+ return None
46
+
47
+ def analyze_location(location_name):
48
+ """Main analysis function with enhanced historical and forecast data"""
49
+ try:
50
  analyzer = TobaccoAnalyzer()
51
  visualizer = VisualizationHandler(analyzer.optimal_conditions)
52
 
53
  # Get coordinates from location name
54
  location_data = analyzer.geocode_location(location_name)
55
  if not location_data:
56
+ return None, None, "Location not found. Please try a different location name.", None
57
 
58
  lat, lon = location_data['lat'], location_data['lon']
59
 
60
+ # Get weather data
61
  df = analyzer.get_weather_data(lat, lon, historical_days=90, forecast_days=90)
62
+ if df is None or df.empty:
63
+ return None, None, "Unable to fetch weather data. Please try again.", None
64
 
65
+ # Get land use data
66
+ osm_data = analyzer.get_osm_data(lat, lon)
 
67
 
68
+ # Calculate scores
69
  historical = df[df['type'] == 'historical']
70
  forecast = df[df['type'].isin(['forecast_5day', 'forecast_extended'])]
71
 
72
+ # Safety checks for None values
73
+ if historical is None or historical.empty:
74
+ return None, None, "No historical data available.", None
75
+
76
+ temp_mean = historical['temperature'].mean()
77
+ humidity_mean = historical['humidity'].mean()
78
+ rainfall_mean = historical['rainfall'].mean()
79
 
80
+ if any(x is None for x in [temp_mean, humidity_mean, rainfall_mean]):
81
+ return None, None, "Invalid weather data received.", None
82
+
83
+ temp_score = np.clip((temp_mean - 15) / (30 - 15), 0, 1)
84
+ humidity_score = np.clip((humidity_mean - 50) / (80 - 50), 0, 1)
85
+ rainfall_score = np.clip(rainfall_mean / 5, 0, 1)
86
 
87
+ # Analyze trends with error handling
88
+ try:
89
+ trends = analyzer.analyze_trends(df)
90
+ trend_factor = 0.1
91
+ temp_score += trends['historical']['temperature']['trend'] * trend_factor
92
+ humidity_score += trends['historical']['humidity']['trend'] * trend_factor
93
+ rainfall_score += trends['historical']['rainfall']['trend'] * trend_factor
94
+ except Exception as e:
95
+ print(f"Error in trend analysis: {e}")
96
+ trends = None
97
 
98
+ # Calculate overall score
99
+ overall_score = np.mean([temp_score, humidity_score, rainfall_score])
100
  overall_score = np.clip(overall_score, 0, 1)
101
 
102
+ # Create visualizations
103
  time_series_plot = visualizer.create_interactive_plots(df)
104
  gauge_plot = visualizer.create_gauge_chart(overall_score)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
105
 
106
+ # Create enhanced map with OSM data
107
+ m = folium.Map(location=[lat, lon], zoom_start=13)
108
+
109
+ # Add weather data overlay
110
+ radius = 2000 # 2km radius
111
+ color = get_score_color(overall_score)
112
+
113
+ folium.Circle(
114
+ radius=radius,
115
+ location=[lat, lon],
116
+ popup=f'Growing Suitability Score: {overall_score:.2f}',
117
+ color=color,
118
+ fill=True,
119
+ fillOpacity=0.4
120
+ ).add_to(m)
121
+
122
+ # Add heatmap if we have enough data points
123
+ if not historical.empty and len(historical) > 0:
124
+ heat_data = [[row['temperature'], row['humidity'], row['rainfall']]
125
+ for _, row in historical.iterrows()]
126
+ plugins.HeatMap(heat_data).add_to(m)
127
+
128
+ # Generate analysis text with safe string formatting
129
  analysis_text = f"""
130
  ๐Ÿ“ Location Analysis:
131
+ Location: {location_data.get('address', 'Unknown')}
132
  Coordinates: {lat:.4f}ยฐN, {lon:.4f}ยฐE
133
 
134
  ๐ŸŒก๏ธ Historical Weather Analysis (Past 90 Days):
135
+ Temperature: {temp_mean:.1f}ยฐC
136
+ Humidity: {humidity_mean:.1f}%
137
+ Rainfall: {rainfall_mean:.1f}mm/day
138
 
139
  ๐Ÿ“ˆ Weather Trends:
140
+ """
 
 
141
 
142
+ if trends:
143
+ analysis_text += f"""
144
+ Temperature Trend: {trends['historical']['temperature']['trend']:.3f}ยฐC/day
145
+ Humidity Trend: {trends['historical']['humidity']['trend']:.3f}%/day
146
+ Rainfall Trend: {trends['historical']['rainfall']['trend']:.3f}mm/day
147
+ """
148
 
149
+ if not forecast.empty:
150
+ analysis_text += f"""
151
+ ๐Ÿ”ฎ Forecast Analysis (Next 90 Days):
152
+ Temperature: {forecast['temperature'].mean():.1f}ยฐC
153
+ Humidity: {forecast['humidity'].mean():.1f}%
154
+ Rainfall: {forecast['rainfall'].mean():.1f}mm/day
155
+ """
156
 
157
+ analysis_text += f"""
158
+ ๐Ÿ“Š Growing Condition Scores:
159
  Temperature Score: {temp_score:.2f}
160
  Humidity Score: {humidity_score:.2f}
161
  Rainfall Score: {rainfall_score:.2f}
 
162
  Overall Score: {overall_score:.2f}
163
 
164
+ ๐ŸŽฏ Recommendation: {get_recommendation(overall_score)}
 
 
 
 
165
  """
166
 
167
+ return time_series_plot, gauge_plot, analysis_text, m._repr_html_()
168
 
169
  except Exception as e:
170
  error_message = f"An error occurred: {str(e)}"
171
+ return None, None, error_message, None
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
172
 
173
+ def get_score_color(score):
174
+ """Get color based on score"""
175
  if score >= 0.8:
176
+ return 'green'
177
  elif score >= 0.6:
178
+ return 'yellow'
179
  elif score >= 0.4:
180
+ return 'orange'
181
+ return 'red'
 
182
 
183
+ def get_recommendation(score):
184
+ """Get recommendation based on score"""
185
  if score >= 0.8:
186
+ return "โœ… Excellent conditions for tobacco growing!"
187
  elif score >= 0.6:
188
+ return "๐Ÿ‘ Good conditions. Monitor weather patterns."
189
  elif score >= 0.4:
190
+ return "โš ๏ธ Marginal conditions. Consider interventions."
191
+ return "โŒ Poor conditions. Not recommended for planting."
 
192
 
193
+ # Create Gradio interface
194
+ with gr.Blocks() as iface:
 
 
 
 
 
 
 
 
 
195
  gr.Markdown(
196
  """
197
+ # ๐ŸŒฑ Tobacco Growth Prediction for Credit Scoring
198
+ """
 
 
199
  )
200
 
201
  with gr.Row():
202
+ location_input = gr.Textbox(
203
+ label="Enter Location",
204
+ placeholder="e.g., Tabora, Tanzania",
205
+ lines=1
206
+ )
207
+ analyze_button = gr.Button("๐Ÿ“Š Analyze")
 
 
208
 
209
+ with gr.Row():
210
+ analysis_text = gr.Textbox(
211
+ label="Analysis Results",
212
+ lines=15,
213
+ interactive=False
214
+ )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
215
 
216
+ with gr.Row():
217
+ weather_plot = gr.Plot(label="Weather Analysis")
 
 
 
 
218
 
219
+ with gr.Row():
220
+ with gr.Column(scale=1):
221
+ score_gauge = gr.Plot(label="Growing Conditions Score")
222
+ with gr.Column(scale=1):
223
+ location_map = gr.HTML(label="Location Analysis")
224
 
225
+ # Set up the example handling
226
+ examples = gr.Examples(
227
  examples=[
228
  ["Tabora, Tanzania"],
229
  ["Urambo, Tabora, Tanzania"],
 
231
  ["Nzega, Tabora, Tanzania"]
232
  ],
233
  inputs=location_input,
234
+ outputs=[weather_plot, score_gauge, analysis_text, location_map],
235
  fn=analyze_location,
236
  cache_examples=True,
237
+ preprocess=True
238
+ )
239
+
240
+ # Handle button click
241
+ analyze_button.click(
242
+ fn=analyze_location,
243
+ inputs=[location_input],
244
+ outputs=[weather_plot, score_gauge, analysis_text, location_map]
245
  )
246
 
247
  # Launch the app