diff --git a/aprsd/cmds/webchat.py b/aprsd/cmds/webchat.py index e090787..1900b95 100644 --- a/aprsd/cmds/webchat.py +++ b/aprsd/cmds/webchat.py @@ -1,7 +1,6 @@ import datetime import json import logging -import math import signal import sys import threading @@ -14,13 +13,14 @@ from flask_httpauth import HTTPBasicAuth from flask_socketio import Namespace, SocketIO from geopy.distance import geodesic from oslo_config import cfg +import timeago from werkzeug.security import check_password_hash, generate_password_hash import wrapt import aprsd -from aprsd import ( - cli_helper, client, packets, plugin_utils, stats, threads, utils, -) +from aprsd import cli_helper, client, packets, plugin_utils, stats, threads +from aprsd import utils +from aprsd import utils as aprsd_utils from aprsd.client import client_factory, kiss from aprsd.main import cli from aprsd.threads import aprsd as aprsd_threads @@ -131,47 +131,6 @@ def verify_password(username, password): return username -def calculate_initial_compass_bearing(point_a, point_b): - """ - Calculates the bearing between two points. - The formulae used is the following: - θ = atan2(sin(Δlong).cos(lat2), - cos(lat1).sin(lat2) − sin(lat1).cos(lat2).cos(Δlong)) - :Parameters: - - `pointA: The tuple representing the latitude/longitude for the - first point. Latitude and longitude must be in decimal degrees - - `pointB: The tuple representing the latitude/longitude for the - second point. Latitude and longitude must be in decimal degrees - :Returns: - The bearing in degrees - :Returns Type: - float - """ - if (type(point_a) is not tuple) or (type(point_b) is not tuple): - raise TypeError("Only tuples are supported as arguments") - - lat1 = math.radians(point_a[0]) - lat2 = math.radians(point_b[0]) - - diff_long = math.radians(point_b[1] - point_a[1]) - - x = math.sin(diff_long) * math.cos(lat2) - y = math.cos(lat1) * math.sin(lat2) - ( - math.sin(lat1) - * math.cos(lat2) * math.cos(diff_long) - ) - - initial_bearing = math.atan2(x, y) - - # Now we have the initial bearing but math.atan2 return values - # from -180° to + 180° which is not what we want for a compass bearing - # The solution is to normalize the initial bearing as shown below - initial_bearing = math.degrees(initial_bearing) - compass_bearing = (initial_bearing + 360) % 360 - - return compass_bearing - - def _build_location_from_repeat(message): # This is a location message Format is # ^ld^callsign:latitude,longitude,altitude,course,speed,timestamp @@ -188,16 +147,19 @@ def _build_location_from_repeat(message): course = float(b[3]) speed = float(b[4]) time = int(b[5]) + compass_bearing = aprsd_utils.degrees_to_cardinal(course) data = { "callsign": callsign, "lat": lat, "lon": lon, "altitude": alt, "course": course, + "compass_bearing": compass_bearing, "speed": speed, "lasttime": time, + "timeago": timeago.format(time), } - LOG.warning(f"Location data from REPEAT {data}") + LOG.debug(f"Location data from REPEAT {data}") return data @@ -208,25 +170,32 @@ def _calculate_location_data(location_data): alt = location_data["altitude"] speed = location_data["speed"] lasttime = location_data["lasttime"] + timeago_str = location_data.get( + "timeago", + timeago.format(lasttime), + ) # now calculate distance from our own location distance = 0 if CONF.webchat.latitude and CONF.webchat.longitude: our_lat = float(CONF.webchat.latitude) our_lon = float(CONF.webchat.longitude) distance = geodesic((our_lat, our_lon), (lat, lon)).kilometers - bearing = calculate_initial_compass_bearing( + bearing = aprsd_utils.calculate_initial_compass_bearing( (our_lat, our_lon), (lat, lon), ) + compass_bearing = aprsd_utils.degrees_to_cardinal(bearing) return { "callsign": location_data["callsign"], "lat": lat, "lon": lon, "altitude": alt, "course": f"{bearing:0.1f}", + "compass_bearing": compass_bearing, "speed": speed, "lasttime": lasttime, - "distance": f"{distance:0.3f}", + "timeago": timeago_str, + "distance": f"{distance:0.1f}", } diff --git a/aprsd/utils/__init__.py b/aprsd/utils/__init__.py index e3ca433..6998d82 100644 --- a/aprsd/utils/__init__.py +++ b/aprsd/utils/__init__.py @@ -174,14 +174,29 @@ def load_entry_points(group): print(traceback.format_exc(), file=sys.stderr) -def calculate_initial_compass_bearing(start, end): - if (type(start) != tuple) or (type(end) != tuple): # noqa: E721 +def calculate_initial_compass_bearing(point_a, point_b): + """ + Calculates the bearing between two points. + The formulae used is the following: + θ = atan2(sin(Δlong).cos(lat2), + cos(lat1).sin(lat2) − sin(lat1).cos(lat2).cos(Δlong)) + :Parameters: + - `pointA: The tuple representing the latitude/longitude for the + first point. Latitude and longitude must be in decimal degrees + - `pointB: The tuple representing the latitude/longitude for the + second point. Latitude and longitude must be in decimal degrees + :Returns: + The bearing in degrees + :Returns Type: + float + """ + if (type(point_a) != tuple) or (type(point_b) != tuple): # noqa: E721 raise TypeError("Only tuples are supported as arguments") - lat1 = math.radians(float(start[0])) - lat2 = math.radians(float(end[0])) + lat1 = math.radians(float(point_a[0])) + lat2 = math.radians(float(point_b[0])) - diff_long = math.radians(float(end[1]) - float(start[1])) + diff_long = math.radians(float(point_b[1]) - float(point_a[1])) x = math.sin(diff_long) * math.cos(lat2) y = math.cos(lat1) * math.sin(lat2) - ( diff --git a/aprsd/web/chat/static/js/send-message.js b/aprsd/web/chat/static/js/send-message.js index dfd7d88..f93d1a8 100644 --- a/aprsd/web/chat/static/js/send-message.js +++ b/aprsd/web/chat/static/js/send-message.js @@ -17,25 +17,24 @@ function reload_popovers() { } function build_location_string(msg) { - dt = new Date(parseInt(msg['lasttime']) * 1000); - loc = "Last Location Update: " + dt.toLocaleString(); - loc += "
Latitude: " + msg['lat'] + "
Longitude: " + msg['lon']; - loc += "
" + "Altitude: " + msg['altitude'] + " m"; - loc += "
" + "Speed: " + msg['speed'] + " kph"; - loc += "
" + "Bearing: " + msg['course'] + "°"; - loc += "
" + "distance: " + msg['distance'] + " km"; - return loc; + dt = new Date(parseInt(msg['lasttime']) * 1000); + loc = "Last Location Update: " + dt.toLocaleString(); + loc += "
Latitude: " + msg['lat'] + "
Longitude: " + msg['lon']; + loc += "
" + "Altitude: " + msg['altitude'] + " m"; + loc += "
" + "Speed: " + msg['speed'] + " kph"; + loc += "
" + "Bearing: " + msg['compass_bearing']; + loc += "
" + "distance: " + msg['distance'] + " km"; + return loc; } function build_location_string_small(msg) { - - dt = new Date(parseInt(msg['lasttime']) * 1000); - + dt = new Date(parseInt(msg['lasttime']) * 1000); loc = "" + msg['distance'] + "km"; //loc += "Lat " + msg['lat'] + " Lon " + msg['lon']; - loc += "@" + msg['course'] + "°"; + loc += " " + msg['compass_bearing']; //loc += " Distance " + msg['distance'] + " km"; - loc += " " + dt.toLocaleString(); + //loc += " " + dt.toLocaleString(); + loc += " " + msg['timeago']; return loc; } @@ -346,9 +345,9 @@ function create_callsign_tab_content(callsign, active=false) { item_html += '
'; item_html += '
'; item_html += '
'; - item_html += ''; - item_html += ' '+location_str+'
'; + item_html += ''; + item_html += ' '+location_str+'
'; item_html += '
'; item_html += '
'; item_html += ''; diff --git a/requirements-dev.txt b/requirements-dev.txt index f741215..2683e49 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -34,7 +34,7 @@ imagesize==1.4.1 # via sphinx iniconfig==2.0.0 # via pytest isort==5.13.2 # via -r requirements-dev.in, gray jinja2==3.1.4 # via sphinx -libcst==1.5.0 # via fixit +libcst==1.5.1 # via fixit m2r==0.3.1 # via -r requirements-dev.in markupsafe==3.0.2 # via jinja2 mccabe==0.7.0 # via flake8 diff --git a/requirements.in b/requirements.in index e7b4e4d..0491d16 100644 --- a/requirements.in +++ b/requirements.in @@ -27,3 +27,4 @@ tzlocal update_checker wrapt pytz +timeago diff --git a/requirements.txt b/requirements.txt index ae25fb0..11c0bed 100644 --- a/requirements.txt +++ b/requirements.txt @@ -37,8 +37,8 @@ markupsafe==3.0.2 # via jinja2, werkzeug marshmallow==3.23.1 # via dataclasses-json mypy-extensions==1.0.0 # via typing-inspect netaddr==1.3.0 # via oslo-config -oslo-config==9.6.0 # via -r requirements.in -oslo-i18n==6.4.0 # via oslo-config +oslo-config==9.7.0 # via -r requirements.in +oslo-i18n==6.5.0 # via oslo-config packaging==24.2 # via marshmallow pbr==6.1.0 # via oslo-i18n, stevedore pluggy==1.5.0 # via -r requirements.in @@ -57,9 +57,10 @@ shellingham==1.5.4 # via -r requirements.in simple-websocket==1.1.0 # via python-engineio six==1.16.0 # via -r requirements.in soupsieve==2.6 # via beautifulsoup4 -stevedore==5.3.0 # via oslo-config +stevedore==5.4.0 # via oslo-config tabulate==0.9.0 # via -r requirements.in thesmuggler==1.0.1 # via -r requirements.in +timeago==1.0.16 # via -r requirements.in typing-extensions==4.12.2 # via typing-inspect typing-inspect==0.9.0 # via dataclasses-json tzlocal==5.2 # via -r requirements.in