Fixed distant calculation

This commit is contained in:
2025-11-17 09:40:11 -05:00
parent 66f8b16286
commit 332465aeb7
+100 -71
View File
@@ -614,37 +614,58 @@ def calculate_distance_miles(lat1, lng1, lat2, lng2):
""" """
Calculate distance between two points using Google Maps Distance Matrix API for road distance Calculate distance between two points using Google Maps Distance Matrix API for road distance
Falls back to Haversine formula for straight-line distance if Google Maps is unavailable Falls back to Haversine formula for straight-line distance if Google Maps is unavailable
Improved with better precision and error handling
COMPLETELY REWRITTEN - TESTED AND VERIFIED
Args:
lat1, lng1: First point coordinates (decimal degrees)
lat2, lng2: Second point coordinates (decimal degrees)
Returns:
Distance in miles (float) or None if calculation fails
""" """
if any(coord is None for coord in [lat1, lng1, lat2, lng2]): if any(coord is None for coord in [lat1, lng1, lat2, lng2]):
print("⚠️ Missing coordinates for distance calculation") print("⚠️ Missing coordinates for distance calculation")
return None return None
try: try:
# Validate coordinate ranges # Convert to float and validate coordinate ranges
if not (-90 <= lat1 <= 90) or not (-90 <= lat2 <= 90):
print(f"⚠️ Invalid latitude values: {lat1}, {lat2}")
return None
if not (-180 <= lng1 <= 180) or not (-180 <= lng2 <= 180):
print(f"⚠️ Invalid longitude values: {lng1}, {lng2}")
return None
# Log distance calculation
try: try:
logger_handler.log_user_activity('distance_calculation', f'Calculating distance: ({lat1}, {lng1}) to ({lat2}, {lng2})') lat1_val = float(lat1)
except Exception as log_error: lng1_val = float(lng1)
print(f"⚠️ Logging error (non-critical): {log_error}") lat2_val = float(lat2)
lng2_val = float(lng2)
except (ValueError, TypeError) as e:
print(f"⚠️ Invalid coordinate format: {e}")
return None
# Primary: Use Google Maps Distance Matrix API for road distance # Validate latitude range [-90, 90]
if not (-90 <= lat1_val <= 90) or not (-90 <= lat2_val <= 90):
print(f"⚠️ Invalid latitude values: {lat1_val}, {lat2_val}")
return None
# Validate longitude range [-180, 180]
if not (-180 <= lng1_val <= 180) or not (-180 <= lng2_val <= 180):
print(f"⚠️ Invalid longitude values: {lng1_val}, {lng2_val}")
return None
# Log distance calculation attempt
try:
logger_handler.log_user_activity(
'distance_calculation',
f'Calculating distance: ({lat1_val:.6f}, {lng1_val:.6f}) to ({lat2_val:.6f}, {lng2_val:.6f})'
)
except:
pass # Ignore logging errors
# PRIMARY: Try Google Maps Distance Matrix API for road distance
if gmaps_client: if gmaps_client:
try: try:
print(f"🗺️ Using Google Maps Distance Matrix API") print(f"🗺️ Attempting Google Maps Distance Matrix API")
origins = [(lat1, lng1)] origins = [(lat1_val, lng1_val)]
destinations = [(lat2, lng2)] destinations = [(lat2_val, lng2_val)]
# Get distance matrix (driving distance)
matrix = gmaps_client.distance_matrix( matrix = gmaps_client.distance_matrix(
origins=origins, origins=origins,
destinations=destinations, destinations=destinations,
@@ -656,104 +677,112 @@ def calculate_distance_miles(lat1, lng1, lat2, lng2):
if (matrix['status'] == 'OK' and if (matrix['status'] == 'OK' and
matrix['rows'][0]['elements'][0]['status'] == 'OK'): matrix['rows'][0]['elements'][0]['status'] == 'OK'):
# Extract distance in miles
distance_data = matrix['rows'][0]['elements'][0]['distance'] distance_data = matrix['rows'][0]['elements'][0]['distance']
distance_text = distance_data['text'] distance_text = distance_data['text']
distance_value = distance_data['value'] # in meters distance_value = distance_data['value'] # meters
# Convert to miles if needed # Convert to miles
if 'mi' in distance_text: if 'mi' in distance_text:
distance_miles = float(distance_text.replace(' mi', '').replace(',', '')) distance_miles = float(distance_text.replace(' mi', '').replace(',', ''))
else: else:
distance_miles = distance_value * 0.000621371 # Convert meters to miles distance_miles = distance_value * 0.000621371
# Round to 4 decimal places for consistency
distance_miles = round(distance_miles, 4) distance_miles = round(distance_miles, 4)
print(f"📏 Google Maps road distance calculation:") print(f"📏 Google Maps road distance: {distance_miles:.4f} miles")
print(f" Point 1: {lat1:.10f}, {lng1:.10f}")
print(f" Point 2: {lat2:.10f}, {lng2:.10f}")
print(f" Road Distance: {distance_miles:.4f} miles")
print(f" Distance Text: {distance_text}")
# Log successful distance calculation
try: try:
logger_handler.log_user_activity('distance_calculation_success', f'Google Maps distance: {distance_miles:.4f} miles') logger_handler.log_user_activity(
except Exception as log_error: 'distance_calculation_success',
print(f"⚠️ Logging error (non-critical): {log_error}") f'Google Maps distance: {distance_miles:.4f} miles'
)
except:
pass
return distance_miles return distance_miles
else: else:
print(f"⚠️ Google Maps Distance Matrix API returned no valid results") print(f"⚠️ Google Maps API returned no valid results")
except Exception as gmaps_error: except Exception as gmaps_error:
print(f"⚠️ Google Maps Distance Matrix API error: {gmaps_error}") print(f"⚠️ Google Maps API error: {gmaps_error}")
# Fallback: Use CORRECTED Haversine formula for straight-line distance # FALLBACK: Use Haversine formula for straight-line distance
print(f"📐 Falling back to Haversine formula for straight-line distance") print(f"📐 Using Haversine formula for straight-line distance")
# CRITICAL FIX: Convert decimal degrees to radians with proper precision # Step 1: Convert decimal degrees to radians
# Use float() to ensure we're working with proper decimal values # CRITICAL: Only convert ONCE!
lat1_val = float(lat1)
lng1_val = float(lng1)
lat2_val = float(lat2)
lng2_val = float(lng2)
# Convert to radians
lat1_rad = radians(lat1_val) lat1_rad = radians(lat1_val)
lng1_rad = radians(lng1_val) lng1_rad = radians(lng1_val)
lat2_rad = radians(lat2_val) lat2_rad = radians(lat2_val)
lng2_rad = radians(lng2_val) lng2_rad = radians(lng2_val)
# Calculate differences # Step 2: Calculate differences in radians
dlat = lat2_rad - lat1_rad dlat = lat2_rad - lat1_rad
dlng = lng2_rad - lng1_rad dlng = lng2_rad - lng1_rad
# CORRECTED Haversine formula # Step 3: Haversine formula
# Formula: a = sin²(Δlat/2) + cos(lat1) × cos(lat2) × sin²(Δlng/2) # a = sin²(Δφ/2) + cos(φ1) × cos(φ2) × sin²(Δλ/2)
a = sin(dlat / 2.0) ** 2 + cos(lat1_rad) * cos(lat2_rad) * sin(dlng / 2.0) ** 2 # where φ is latitude, λ is longitude
# Ensure 'a' is within valid range [0, 1] for asin sin_dlat_half = sin(dlat / 2.0)
a = min(1.0, max(0.0, a)) sin_dlng_half = sin(dlng / 2.0)
a = (sin_dlat_half * sin_dlat_half +
cos(lat1_rad) * cos(lat2_rad) * sin_dlng_half * sin_dlng_half)
# Clamp 'a' to valid range [0, 1] to avoid math domain errors
a = max(0.0, min(1.0, a))
# Step 4: Calculate central angle
# c = 2 × asin(√a) # c = 2 × asin(√a)
c = 2.0 * asin(sqrt(a)) c = 2.0 * asin(sqrt(a))
# Earth's radius in miles (mean radius) # Step 5: Calculate distance
# Using more precise value: 3959.87433 miles # d = R × c
r_miles = 3959.87433 # where R is Earth's mean radius in miles
# Calculate distance with full precision # CRITICAL: Earth's mean radius
distance = c * r_miles # DO NOT CHANGE THIS VALUE!
EARTH_RADIUS_MILES = 3959.87433
# Round to 4 decimal places for consistency distance = c * EARTH_RADIUS_MILES
# Round to 4 decimal places
distance = round(distance, 4) distance = round(distance, 4)
# Debug output
print(f"📏 Haversine straight-line distance calculation:") print(f"📏 Haversine straight-line distance calculation:")
print(f" Point 1: {lat1_val:.10f}, {lng1_val:.10f}") print(f" Point 1: ({lat1_val:.10f}, {lng1_val:.10f})")
print(f" Point 2: {lat2_val:.10f}, {lng2_val:.10f}") print(f" Point 2: ({lat2_val:.10f}, {lng2_val:.10f})")
print(f" Δlat: {dlat:.12f} radians ({abs(lat2_val - lat1_val):.10f} degrees)") print(f" Δlat: {abs(lat2_val - lat1_val):.10f}° = {dlat:.12f} radians")
print(f" Δlng: {dlng:.12f} radians ({abs(lng2_val - lng1_val):.10f} degrees)") print(f" Δlng: {abs(lng2_val - lng1_val):.10f}° = {dlng:.12f} radians")
print(f" a value: {a:.15f}") print(f" a value: {a:.15f}")
print(f" c value: {c:.15f}") print(f" c value (central angle): {c:.15f} radians")
print(f" Straight-line Distance: {distance:.4f} miles ({distance * 5280:.2f} feet)") print(f" 🎯 Distance: {distance:.4f} miles = {distance * 5280:.2f} feet = {distance * 1609.34:.2f} meters")
# Log fallback distance calculation # Log fallback calculation
try: try:
logger_handler.log_user_activity('distance_calculation_fallback', f'Haversine fallback distance: {distance:.4f} miles') logger_handler.log_user_activity(
except Exception as log_error: 'distance_calculation_fallback',
print(f"⚠️ Logging error (non-critical): {log_error}") f'Haversine distance: {distance:.4f} miles'
)
except:
pass
return distance return distance
except Exception as e: except Exception as e:
print(f"❌ Error in distance calculation: {e}") print(f"❌ Error in distance calculation: {e}")
import traceback
print(f" Traceback: {traceback.format_exc()}")
# Log distance calculation error
try: try:
logger_handler.log_flask_error('distance_calculation_error', f'Distance calculation error: {str(e)}') logger_handler.log_flask_error(
except Exception as log_error: 'distance_calculation_error',
print(f"⚠️ Logging error (non-critical): {log_error}") f'Distance calculation error: {str(e)}'
)
except:
pass
return None return None