@@ -258,50 +258,50 @@ def _prepare_ml_features(history_data: List[Dict[str, Any]], weather_df: pd.Data
258258 'is_northern_region' , 'is_growing_season' ]]
259259
260260
261- def _calculate_flowering_start_date_ml (forecast_data : pd .DataFrame , method : str = 'ndvi_threshold' ) -> Dict [str , Any ]:
261+ def _calculate_blooming_start_date_ml (forecast_data : pd .DataFrame , method : str = 'ndvi_threshold' ) -> Dict [str , Any ]:
262262 """
263- Calculate flowering start date based on ML NDVI predictions
263+ Calculate blooming start date based on ML NDVI predictions
264264
265265 Parameters:
266266 forecast_data (pd.DataFrame): Prophet forecast with 'ds' and 'yhat' columns
267- method (str): Method for calculating flowering start
267+ method (str): Method for calculating blooming start
268268
269269 Returns:
270- dict: Flowering prediction results
270+ dict: Blooming prediction results
271271 """
272272 if forecast_data is None or len (forecast_data ) == 0 :
273- return {'flowering_start_date ' : None , 'method' : method , 'confidence' : 0 }
273+ return {'blooming_start_date ' : None , 'method' : method , 'confidence' : 0 }
274274
275275 # Create a copy and ensure proper date handling
276276 df = forecast_data .copy ()
277277 df ['ds' ] = pd .to_datetime (df ['ds' ])
278278 df = df .sort_values ('ds' )
279279
280- flowering_results = {}
280+ blooming_results = {}
281281
282282 if method == 'ndvi_threshold' :
283- # Method 1: Flowering starts when NDVI reaches 0.6-0.7
284- flowering_threshold = 0.65
283+ # Method 1: Blooming starts when NDVI reaches 0.6-0.7
284+ blooming_threshold = 0.65
285285
286286 # Find first date when NDVI exceeds threshold (during spring growth)
287287 spring_mask = (df ['ds' ].dt .month >= 3 ) & (df ['ds' ].dt .month <= 6 )
288288 spring_data = df [spring_mask ]
289289
290290 if len (spring_data ) > 0 :
291- flowering_candidates = spring_data [spring_data ['yhat' ] >= flowering_threshold ]
292- if len (flowering_candidates ) > 0 :
293- flowering_date = flowering_candidates ['ds' ].iloc [0 ]
294- confidence = min (0.9 , 0.5 + (flowering_candidates ['yhat' ].iloc [0 ] - flowering_threshold ) * 2 )
295- flowering_results = {
296- 'flowering_start_date ' : flowering_date ,
291+ blooming_candidates = spring_data [spring_data ['yhat' ] >= blooming_threshold ]
292+ if len (blooming_candidates ) > 0 :
293+ blooming_date = blooming_candidates ['ds' ].iloc [0 ]
294+ confidence = min (0.9 , 0.5 + (blooming_candidates ['yhat' ].iloc [0 ] - blooming_threshold ) * 2 )
295+ blooming_results = {
296+ 'blooming_start_date ' : blooming_date ,
297297 'method' : 'NDVI Threshold (≥0.65)' ,
298298 'confidence' : confidence ,
299- 'ndvi_at_flowering ' : flowering_candidates ['yhat' ].iloc [0 ],
300- 'threshold_used' : flowering_threshold
299+ 'ndvi_at_blooming ' : blooming_candidates ['yhat' ].iloc [0 ],
300+ 'threshold_used' : blooming_threshold
301301 }
302302
303303 elif method == 'ndvi_acceleration' :
304- # Method 2: Flowering starts when NDVI growth rate accelerates
304+ # Method 2: Blooming starts when NDVI growth rate accelerates
305305 if len (df ) >= 3 :
306306 df ['ndvi_growth' ] = df ['yhat' ].diff ()
307307 df ['ndvi_acceleration' ] = df ['ndvi_growth' ].diff ()
@@ -312,31 +312,31 @@ def _calculate_flowering_start_date_ml(forecast_data: pd.DataFrame, method: str
312312 if len (spring_data ) > 0 :
313313 max_accel_idx = spring_data ['ndvi_acceleration' ].idxmax ()
314314 if not pd .isna (max_accel_idx ):
315- flowering_date = df .loc [max_accel_idx , 'ds' ]
315+ blooming_date = df .loc [max_accel_idx , 'ds' ]
316316 confidence = min (0.85 , 0.6 + abs (df .loc [max_accel_idx , 'ndvi_acceleration' ]) * 10 )
317- flowering_results = {
318- 'flowering_start_date ' : flowering_date ,
317+ blooming_results = {
318+ 'blooming_start_date ' : blooming_date ,
319319 'method' : 'NDVI Acceleration Peak' ,
320320 'confidence' : confidence ,
321- 'ndvi_at_flowering ' : df .loc [max_accel_idx , 'yhat' ],
321+ 'ndvi_at_blooming ' : df .loc [max_accel_idx , 'yhat' ],
322322 'acceleration_value' : df .loc [max_accel_idx , 'ndvi_acceleration' ]
323323 }
324324
325325 # Default return if no method worked
326- if not flowering_results :
327- flowering_results = {
328- 'flowering_start_date ' : None ,
326+ if not blooming_results :
327+ blooming_results = {
328+ 'blooming_start_date ' : None ,
329329 'method' : method ,
330330 'confidence' : 0 ,
331- 'error' : 'Unable to determine flowering date with selected method'
331+ 'error' : 'Unable to determine blooming date with selected method'
332332 }
333333
334- return flowering_results
334+ return blooming_results
335335
336336
337337def _predict_ndvi_with_ml (history_data : List [Dict [str , Any ]], weather_df : pd .DataFrame ,
338338 target_year : int ) -> Dict [str , Any ]:
339- """Use ML model to predict NDVI and flowering dates."""
339+ """Use ML model to predict NDVI and blooming dates."""
340340 try :
341341 model = _load_ml_model ()
342342 if model is None :
@@ -424,18 +424,18 @@ def _predict_ndvi_with_ml(history_data: List[Dict[str, Any]], weather_df: pd.Dat
424424 # Make predictions
425425 forecast = model .predict (future_df )
426426
427- # Calculate flowering dates using multiple methods
427+ # Calculate blooming dates using multiple methods
428428 methods = ['ndvi_threshold' , 'ndvi_acceleration' ]
429- flowering_predictions = {}
429+ blooming_predictions = {}
430430
431431 for method in methods :
432- result = _calculate_flowering_start_date_ml (forecast , method )
433- flowering_predictions [method ] = result
432+ result = _calculate_blooming_start_date_ml (forecast , method )
433+ blooming_predictions [method ] = result
434434
435435 # Select best prediction (highest confidence)
436- best_method = max (flowering_predictions .keys (),
437- key = lambda x : flowering_predictions [x ]['confidence' ])
438- best_prediction = flowering_predictions [best_method ]
436+ best_method = max (blooming_predictions .keys (),
437+ key = lambda x : blooming_predictions [x ]['confidence' ])
438+ best_prediction = blooming_predictions [best_method ]
439439
440440 # Find NDVI peak from predictions
441441 ndvi_values = forecast ['yhat' ].values
@@ -454,9 +454,9 @@ def _predict_ndvi_with_ml(history_data: List[Dict[str, Any]], weather_df: pd.Dat
454454 return {
455455 'ndvi_peak' : float (ndvi_peak ) if ndvi_peak is not None else None ,
456456 'ndvi_peak_at' : ndvi_peak_at ,
457- 'flowering_start_date ' : best_prediction .get ('flowering_start_date ' ),
458- 'flowering_confidence ' : best_prediction .get ('confidence' , 0 ),
459- 'flowering_method ' : best_prediction .get ('method' ),
457+ 'blooming_start_date ' : best_prediction .get ('blooming_start_date ' ),
458+ 'blooming_confidence ' : best_prediction .get ('confidence' , 0 ),
459+ 'blooming_method ' : best_prediction .get ('method' ),
460460 'model' : 'prophet_ml' ,
461461 'predictions' : forecast [['ds' , 'yhat' , 'yhat_lower' , 'yhat_upper' ]].to_dict ('records' )
462462 }
@@ -466,9 +466,9 @@ def _predict_ndvi_with_ml(history_data: List[Dict[str, Any]], weather_df: pd.Dat
466466 return {
467467 'ndvi_peak' : None ,
468468 'ndvi_peak_at' : None ,
469- 'flowering_start_date ' : None ,
470- 'flowering_confidence ' : 0 ,
471- 'flowering_method ' : None ,
469+ 'blooming_start_date ' : None ,
470+ 'blooming_confidence ' : 0 ,
471+ 'blooming_method ' : None ,
472472 'model' : 'heuristic_fallback' ,
473473 'error' : str (exc )
474474 }
@@ -863,8 +863,8 @@ def _compute_row(item: Tuple[int, Any]) -> Optional[Dict[str, Any]]:
863863 monitor .logger .info ("Forecast NDVI peak %.3f" , ml_results .get ('ndvi_peak' , 0.0 ))
864864 ndvi_peak = ml_results .get ('ndvi_peak' )
865865 ndvi_peak_at = ml_results .get ('ndvi_peak_at' )
866- flowering_start_date = ml_results .get ('flowering_start_date ' )
867- flowering_confidence = ml_results .get ('flowering_confidence ' , 0 )
866+ blooming_start_date = ml_results .get ('blooming_start_date ' )
867+ blooming_confidence = ml_results .get ('blooming_confidence ' , 0 )
868868 model_name = "prophet_ml"
869869
870870 # Add ML predictions to history with type 1
@@ -913,9 +913,9 @@ def _compute_row(item: Tuple[int, Any]) -> Optional[Dict[str, Any]]:
913913 if match_ratio < min_match :
914914 match_ratio = min (min_match , 1.0 )
915915
916- # Boost confidence if we have flowering prediction
917- if flowering_confidence > match_ratio :
918- match_ratio = min (1.0 , (match_ratio + flowering_confidence ) / 2 )
916+ # Boost confidence if we have blooming prediction
917+ if blooming_confidence > match_ratio :
918+ match_ratio = min (1.0 , (match_ratio + blooming_confidence ) / 2 )
919919
920920 else :
921921 # Fall back to heuristic method
@@ -924,8 +924,8 @@ def _compute_row(item: Tuple[int, Any]) -> Optional[Dict[str, Any]]:
924924 ndvi_values = [entry ["ndvi" ] for entry in history if entry ["ndvi" ] is not None ]
925925 ndvi_peak = max (ndvi_values ) if ndvi_values else None
926926 ndvi_peak_at = None
927- flowering_start_date = None
928- flowering_confidence = 0
927+ blooming_start_date = None
928+ blooming_confidence = 0
929929
930930 if ndvi_peak is not None :
931931 for entry in history :
@@ -964,7 +964,7 @@ def _compute_row(item: Tuple[int, Any]) -> Optional[Dict[str, Any]]:
964964 yield_per_ha = float (yield_profile .get ("yield_t_per_ha" ) or 0.0 )
965965 yield_tph = yield_per_ha * match_ratio
966966
967- # Prepare forecast response with flowering information
967+ # Prepare forecast response with blooming information
968968 forecast_dict = {
969969 "year" : forecast_year ,
970970 "yieldTph" : round (yield_tph , 2 ),
@@ -975,16 +975,16 @@ def _compute_row(item: Tuple[int, Any]) -> Optional[Dict[str, Any]]:
975975 "ndviModel" : model_name ,
976976 }
977977
978- # Add flowering information if available
979- if flowering_start_date is not None :
980- if isinstance (flowering_start_date , pd .Timestamp ):
981- flowering_start_date = flowering_start_date .to_pydatetime ()
982- if isinstance (flowering_start_date , datetime ):
983- if flowering_start_date .tzinfo is None :
984- flowering_start_date = flowering_start_date .replace (tzinfo = timezone .utc )
985- forecast_dict ["ndviStartAt" ] = _isoformat_utc (flowering_start_date )
986- forecast_dict ["ndviStartConfidence" ] = round (flowering_confidence , 2 )
987- forecast_dict ["ndviStartMethod" ] = ml_results .get ('flowering_method ' )
978+ # Add blooming information if available
979+ if blooming_start_date is not None :
980+ if isinstance (blooming_start_date , pd .Timestamp ):
981+ blooming_start_date = blooming_start_date .to_pydatetime ()
982+ if isinstance (blooming_start_date , datetime ):
983+ if blooming_start_date .tzinfo is None :
984+ blooming_start_date = blooming_start_date .replace (tzinfo = timezone .utc )
985+ forecast_dict ["ndviStartAt" ] = _isoformat_utc (blooming_start_date )
986+ forecast_dict ["ndviStartConfidence" ] = round (blooming_confidence , 2 )
987+ forecast_dict ["ndviStartMethod" ] = ml_results .get ('blooming_method ' )
988988
989989 anomalies : List [Dict [str , Any ]] = []
990990 try :
0 commit comments