lyimo commited on
Commit
ba85e3f
·
verified ·
1 Parent(s): 85a2beb

Update part1_data.py

Browse files
Files changed (1) hide show
  1. part1_data.py +164 -5
part1_data.py CHANGED
@@ -8,6 +8,8 @@ import requests
8
  from geopy.geocoders import Nominatim
9
  from geopy.exc import GeocoderTimedOut
10
  from scipy import stats
 
 
11
 
12
  # Get API key from environment variable
13
  OPENWEATHER_API_KEY = os.getenv('OPENWEATHER_API_KEY', 'default_key')
@@ -18,7 +20,8 @@ class TobaccoAnalyzer:
18
  self.optimal_conditions = {
19
  'temperature': {'min': 20, 'max': 30},
20
  'humidity': {'min': 60, 'max': 80},
21
- 'rainfall': {'min': 500/365, 'max': 1200/365}
 
22
  }
23
  self.geolocator = Nominatim(user_agent="tobacco_analyzer")
24
  self.seasons = {
@@ -27,6 +30,13 @@ class TobaccoAnalyzer:
27
  7: 'Summer', 8: 'Summer', 9: 'Fall',
28
  10: 'Fall', 11: 'Fall', 12: 'Winter'
29
  }
 
 
 
 
 
 
 
30
 
31
  def geocode_location(self, location_name):
32
  """Convert location name to coordinates"""
@@ -69,7 +79,6 @@ class TobaccoAnalyzer:
69
  # Get forecast data
70
  forecast_data = []
71
  try:
72
- # Get 5-day forecast
73
  forecast_url = f"https://api.openweathermap.org/data/2.5/forecast?lat={lat}&lon={lon}&appid={self.api_key}&units=metric"
74
  response = requests.get(forecast_url)
75
  if response.status_code == 200:
@@ -92,7 +101,6 @@ class TobaccoAnalyzer:
92
  for day in range(1, forecast_days - 5):
93
  date = last_date + timedelta(days=day)
94
 
95
- # Calculate trends from historical data
96
  if not historical_df.empty:
97
  temp_trend = stats.linregress(range(len(historical_df)), historical_df['temperature'])[0]
98
  humidity_trend = stats.linregress(range(len(historical_df)), historical_df['humidity'])[0]
@@ -100,7 +108,6 @@ class TobaccoAnalyzer:
100
  else:
101
  temp_trend = humidity_trend = rainfall_trend = 0
102
 
103
- # Get recent averages
104
  recent_temps = [d['temperature'] for d in forecast_data[-5:]]
105
  recent_humidity = [d['humidity'] for d in forecast_data[-5:]]
106
  recent_rainfall = [d['rainfall'] for d in forecast_data[-5:]]
@@ -133,6 +140,87 @@ class TobaccoAnalyzer:
133
 
134
  return all_data
135
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
136
  def analyze_trends(self, df):
137
  """Analyze weather trends and patterns"""
138
  historical = df[df['type'] == 'historical']
@@ -172,4 +260,75 @@ class TobaccoAnalyzer:
172
  }
173
  }
174
 
175
- return analysis
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8
  from geopy.geocoders import Nominatim
9
  from geopy.exc import GeocoderTimedOut
10
  from scipy import stats
11
+ import ee
12
+ import geemap
13
 
14
  # Get API key from environment variable
15
  OPENWEATHER_API_KEY = os.getenv('OPENWEATHER_API_KEY', 'default_key')
 
20
  self.optimal_conditions = {
21
  'temperature': {'min': 20, 'max': 30},
22
  'humidity': {'min': 60, 'max': 80},
23
+ 'rainfall': {'min': 500/365, 'max': 1200/365},
24
+ 'ndvi': {'min': 0.3, 'max': 0.8} # Optimal NDVI range for tobacco
25
  }
26
  self.geolocator = Nominatim(user_agent="tobacco_analyzer")
27
  self.seasons = {
 
30
  7: 'Summer', 8: 'Summer', 9: 'Fall',
31
  10: 'Fall', 11: 'Fall', 12: 'Winter'
32
  }
33
+ # Initialize Earth Engine
34
+ try:
35
+ ee.Initialize()
36
+ self.ee_initialized = True
37
+ except Exception as e:
38
+ print(f"Error initializing Earth Engine: {e}")
39
+ self.ee_initialized = False
40
 
41
  def geocode_location(self, location_name):
42
  """Convert location name to coordinates"""
 
79
  # Get forecast data
80
  forecast_data = []
81
  try:
 
82
  forecast_url = f"https://api.openweathermap.org/data/2.5/forecast?lat={lat}&lon={lon}&appid={self.api_key}&units=metric"
83
  response = requests.get(forecast_url)
84
  if response.status_code == 200:
 
101
  for day in range(1, forecast_days - 5):
102
  date = last_date + timedelta(days=day)
103
 
 
104
  if not historical_df.empty:
105
  temp_trend = stats.linregress(range(len(historical_df)), historical_df['temperature'])[0]
106
  humidity_trend = stats.linregress(range(len(historical_df)), historical_df['humidity'])[0]
 
108
  else:
109
  temp_trend = humidity_trend = rainfall_trend = 0
110
 
 
111
  recent_temps = [d['temperature'] for d in forecast_data[-5:]]
112
  recent_humidity = [d['humidity'] for d in forecast_data[-5:]]
113
  recent_rainfall = [d['rainfall'] for d in forecast_data[-5:]]
 
140
 
141
  return all_data
142
 
143
+ def get_ndvi_data(self, lat, lon, radius=2000):
144
+ """Get NDVI data for location"""
145
+ try:
146
+ point = ee.Geometry.Point([lon, lat])
147
+ area = point.buffer(radius)
148
+
149
+ end_date = datetime.now()
150
+ start_date = end_date - timedelta(days=90)
151
+
152
+ s2 = ee.ImageCollection('COPERNICUS/S2_SR') \
153
+ .filterDate(start_date.strftime('%Y-%m-%d'), end_date.strftime('%Y-%m-%d')) \
154
+ .filterBounds(area) \
155
+ .filter(ee.Filter.lt('CLOUDY_PIXEL_PERCENTAGE', 20))
156
+
157
+ def addNDVI(image):
158
+ ndvi = image.normalizedDifference(['B8', 'B4']).rename('NDVI')
159
+ return image.addBands(ndvi)
160
+
161
+ s2_ndvi = s2.map(addNDVI)
162
+ ndvi_image = s2_ndvi.select('NDVI').mean()
163
+
164
+ stats = ndvi_image.reduceRegion(
165
+ reducer=ee.Reducer.mean().combine(
166
+ reducer2=ee.Reducer.stdDev(),
167
+ sharedInputs=True
168
+ ).combine(
169
+ reducer2=ee.Reducer.minMax(),
170
+ sharedInputs=True
171
+ ),
172
+ geometry=area,
173
+ scale=10,
174
+ maxPixels=1e9
175
+ ).getInfo()
176
+
177
+ return {
178
+ 'image': ndvi_image,
179
+ 'stats': stats,
180
+ 'area': area
181
+ }
182
+
183
+ except Exception as e:
184
+ print(f"Error fetching NDVI data: {e}")
185
+ return None
186
+
187
+ def analyze_location(self, location_name, historical_days=90, forecast_days=90):
188
+ """Comprehensive location analysis including weather and NDVI"""
189
+ try:
190
+ location_info = self.geocode_location(location_name)
191
+ if not location_info:
192
+ raise ValueError(f"Could not find coordinates for location: {location_name}")
193
+
194
+ lat = location_info['lat']
195
+ lon = location_info['lon']
196
+
197
+ weather_data = self.get_weather_data(lat, lon, historical_days, forecast_days)
198
+ weather_analysis = self.analyze_trends(weather_data)
199
+ weather_score = self.calculate_weather_score(weather_analysis)
200
+
201
+ ndvi_data = None
202
+ ndvi_score = None
203
+ if self.ee_initialized:
204
+ try:
205
+ ndvi_data = self.get_ndvi_data(lat, lon)
206
+ ndvi_score = self.calculate_ndvi_score(ndvi_data)
207
+ except Exception as e:
208
+ print(f"Error getting NDVI data: {e}")
209
+
210
+ return {
211
+ 'location': location_info,
212
+ 'weather_data': weather_data,
213
+ 'weather_analysis': weather_analysis,
214
+ 'weather_score': weather_score,
215
+ 'ndvi_data': ndvi_data,
216
+ 'ndvi_score': ndvi_score,
217
+ 'combined_score': self.calculate_combined_score(weather_score, ndvi_score)
218
+ }
219
+
220
+ except Exception as e:
221
+ print(f"Error in location analysis: {e}")
222
+ return None
223
+
224
  def analyze_trends(self, df):
225
  """Analyze weather trends and patterns"""
226
  historical = df[df['type'] == 'historical']
 
260
  }
261
  }
262
 
263
+ return analysis
264
+
265
+ def calculate_ndvi_score(self, ndvi_data):
266
+ """Calculate a score based on NDVI data"""
267
+ if not ndvi_data or 'stats' not in ndvi_data:
268
+ return None
269
+
270
+ stats = ndvi_data['stats']
271
+ mean_ndvi = stats.get('NDVI_mean', 0)
272
+
273
+ # Convert NDVI from -1:1 scale to 0:1 scale
274
+ score = (mean_ndvi + 1) / 2
275
+
276
+ # Adjust score based on optimal NDVI ranges
277
+ if self.optimal_conditions['ndvi']['min'] <= mean_ndvi <= self.optimal_conditions['ndvi']['max']:
278
+ score *= 1.2 # Bonus for optimal range
279
+ elif mean_ndvi < 0:
280
+ score *= 0.5 # Penalty for very low vegetation
281
+
282
+ return min(1.0, max(0.0, score))
283
+
284
+ def calculate_weather_score(self, weather_analysis):
285
+ """Calculate weather suitability score"""
286
+ if not weather_analysis:
287
+ return None
288
+
289
+ historical = weather_analysis['historical']
290
+
291
+ temp_mean = historical['temperature']['mean']
292
+ humidity_mean = historical['humidity']['mean']
293
+ rainfall_mean = historical['rainfall']['mean']
294
+
295
+ temp_score = self.calculate_range_score(
296
+ temp_mean,
297
+ self.optimal_conditions['temperature']['min'],
298
+ self.optimal_conditions['temperature']['max']
299
+ )
300
+
301
+ humidity_score = self.calculate_range_score(
302
+ humidity_mean,
303
+ self.optimal_conditions['humidity']['min'],
304
+ self.optimal_conditions['humidity']['max']
305
+ )
306
+
307
+ rainfall_score = self.calculate_range_score(
308
+ rainfall_mean,
309
+ self.optimal_conditions['rainfall']['min'],
310
+ self.optimal_conditions['rainfall']['max']
311
+ )
312
+
313
+ return (temp_score * 0.4 + humidity_score * 0.3 + rainfall_score * 0.3)
314
+
315
+ def calculate_range_score(self, value, min_val, max_val):
316
+ """Calculate score based on optimal range"""
317
+ if min_val <= value <= max_val:
318
+ return 1.0
319
+ elif value < min_val:
320
+ return max(0, 1 - (min_val - value) / min_val)
321
+ else:
322
+ return max(0, 1 - (value - max_val) / max_val)
323
+
324
+ def calculate_combined_score(self, weather_score, ndvi_score):
325
+ """Calculate combined suitability score"""
326
+ if weather_score is None:
327
+ return None
328
+ if ndvi_score is None:
329
+ return weather_score
330
+
331
+ weather_weight = 0.6
332
+ ndvi_weight = 0.4
333
+
334
+ return (weather_score * weather_weight) + (ndvi_score * ndvi_weight)