From f2bd7300aaafe4a15d8cf203c5f584129942a626 Mon Sep 17 00:00:00 2001 From: Dan Albert Date: Mon, 24 May 2021 18:06:47 -0700 Subject: [PATCH] Improve precision of kneeboard lat/lon. DMS with decimal seconds is what the hornet uses for PP targest. In the future we'll want to make this aircraft specific (and potentially user preference for jets like the A-10 that can handle both L/L and MGRS). --- game/theater/latlon.py | 25 ++++++++++++++++++++++++- gen/kneeboard.py | 14 ++++++++------ 2 files changed, 32 insertions(+), 7 deletions(-) diff --git a/game/theater/latlon.py b/game/theater/latlon.py index dde2e442..b819e30f 100644 --- a/game/theater/latlon.py +++ b/game/theater/latlon.py @@ -1,5 +1,5 @@ from dataclasses import dataclass -from typing import List +from typing import List, Tuple @dataclass(frozen=True) @@ -9,3 +9,26 @@ class LatLon: def as_list(self) -> List[float]: return [self.latitude, self.longitude] + + @staticmethod + def _components(dimension: float) -> Tuple[int, int, float]: + degrees = int(dimension) + minutes = int(dimension * 60 % 60) + seconds = dimension * 3600 % 60 + return degrees, minutes, seconds + + def _format_component( + self, dimension: float, hemispheres: Tuple[str, str], seconds_precision: int + ) -> str: + hemisphere = hemispheres[0] if dimension >= 0 else hemispheres[1] + degrees, minutes, seconds = self._components(dimension) + return f"{degrees}°{minutes:02}'{seconds:02.{seconds_precision}f}\"{hemisphere}" + + def format_dms(self, include_decimal_seconds: bool = False) -> str: + precision = 2 if include_decimal_seconds else 0 + return " ".join( + [ + self._format_component(self.latitude, ("N", "S"), precision), + self._format_component(self.longitude, ("E", "W"), precision), + ] + ) diff --git a/gen/kneeboard.py b/gen/kneeboard.py index e28ded01..71544a26 100644 --- a/gen/kneeboard.py +++ b/gen/kneeboard.py @@ -36,7 +36,7 @@ from dcs.unittype import FlyingType from tabulate import tabulate from game.data.alic import AlicCodes -from game.db import find_unittype, unit_type_from_name +from game.db import unit_type_from_name from game.theater import ConflictTheater, TheaterGroundObject, LatLon from game.theater.bullseye import Bullseye from game.utils import meters @@ -298,9 +298,7 @@ class BriefingPage(KneeboardPage): headers=["#", "Action", "Alt", "Dist", "GSPD", "Time", "Departure"], ) - writer.text( - f"Bullseye: {self.format_ll(self.bullseye.to_lat_lon(self.theater))}" - ) + writer.text(f"Bullseye: {self.bullseye.to_lat_lon(self.theater).format_dms()}") writer.table( [ @@ -507,7 +505,7 @@ class SeadTaskPage(KneeboardPage): ll = self.theater.point_to_ll(unit.position) unit_type = unit_type_from_name(unit.type) name = unit.name if unit_type is None else unit_type.name - return [name, self.alic_for(unit), self.format_ll(ll)] + return [name, self.alic_for(unit), ll.format_dms(include_decimal_seconds=True)] class StrikeTaskPage(KneeboardPage): @@ -546,7 +544,11 @@ class StrikeTaskPage(KneeboardPage): def target_info_row(self, target: NumberedWaypoint) -> List[str]: ll = self.theater.point_to_ll(target.waypoint.position) - return [str(target.number), target.waypoint.pretty_name, self.format_ll(ll)] + return [ + str(target.number), + target.waypoint.pretty_name, + ll.format_dms(include_decimal_seconds=True), + ] class KneeboardGenerator(MissionInfoGenerator):