Files
LoRa-Mesh-Analyzer/mesh_analyzer/utils.py

76 lines
2.2 KiB
Python

import math
import logging
logger = logging.getLogger(__name__)
def haversine(lat1: float, lon1: float, lat2: float, lon2: float) -> float:
"""
Calculate the great circle distance between two points
on the earth (specified in decimal degrees).
Returns distance in meters.
"""
try:
# Check for None values
if lat1 is None or lon1 is None or lat2 is None or lon2 is None:
return 0
# convert decimal degrees to radians
lon1, lat1, lon2, lat2 = map(math.radians, [float(lon1), float(lat1), float(lon2), float(lat2)])
# haversine formula
dlon = lon2 - lon1
dlat = lat2 - lat1
a = math.sin(dlat/2)**2 + math.cos(lat1) * math.cos(lat2) * math.sin(dlon/2)**2
c = 2 * math.asin(math.sqrt(a))
r = 6371000 # Radius of earth in meters
return c * r
except Exception as e:
logger.debug(f"Error calculating haversine distance: {e}")
return 0
def get_val(obj: object, key: str, default: any = None) -> any:
"""
Safely retrieves a value from an object or dictionary.
Handles nested attribute access if key contains dots (e.g. 'user.id').
"""
try:
# Handle dot notation for nested access
if '.' in key:
parts = key.split('.')
current = obj
for part in parts:
current = get_val(current, part)
if current is None:
return default
return current
if isinstance(obj, dict):
return obj.get(key, default)
return getattr(obj, key, default)
except Exception:
return default
def get_node_name(node: dict, node_id: str = None) -> str:
"""
Helper to get a human-readable name for a node.
"""
user = get_val(node, 'user', {})
long_name = get_val(user, 'longName')
short_name = get_val(user, 'shortName')
if long_name:
return long_name
if short_name:
return short_name
# If no name found, return ID
if node_id:
return node_id
# Try to find ID in user object
user_id = get_val(user, 'id')
if user_id:
return user_id
return "Unknown"