Add proof-of-concept target info kneeboard page.

This is extremely rough and just serves as an example of how to use the
map projection API.
This commit is contained in:
Dan Albert 2021-04-27 21:15:12 -07:00
parent 8a01209ded
commit 2a06a1ffdf
2 changed files with 114 additions and 2 deletions

View File

@ -40,6 +40,7 @@ from dcs.unitgroup import (
VehicleGroup,
)
from dcs.vehicles import AirDefence, Armor, MissilesSS, Unarmed
from pyproj import CRS, Transformer
from shapely import geometry, ops
from gen.flights.flight import FlightType
@ -53,6 +54,7 @@ from .controlpoint import (
OffMapSpawn,
)
from .landmap import Landmap, load_landmap, poly_contains
from .projections import TransverseMercator
from ..point_with_heading import PointWithHeading
from ..utils import Distance, meters, nautical_miles, pairwise
@ -473,6 +475,12 @@ class ReferencePoint:
image_coordinates: Point
@dataclass(frozen=True)
class LatLon:
latitude: float
longitude: float
class ConflictTheater:
terrain: Terrain
@ -725,6 +733,22 @@ class ConflictTheater:
MizCampaignLoader(directory / miz, t).populate_theater()
return t
@property
def projection_parameters(self) -> TransverseMercator:
raise NotImplementedError
def point_to_ll(self, point: Point) -> LatLon:
lat, lon = Transformer.from_crs(
self.projection_parameters.to_crs(), CRS("WGS84")
).transform(point.x, point.y)
return LatLon(lat, lon)
def ll_to_point(self, ll: LatLon) -> Point:
x, y = Transformer.from_crs(
CRS("WGS84"), self.projection_parameters.to_crs()
).transform(ll.latitude, ll.longitude)
return Point(x, y)
class CaucasusTheater(ConflictTheater):
terrain = caucasus.Caucasus()
@ -742,6 +766,12 @@ class CaucasusTheater(ConflictTheater):
"night": (0, 5),
}
@property
def projection_parameters(self) -> TransverseMercator:
from .caucasus import PARAMETERS
return PARAMETERS
class PersianGulfTheater(ConflictTheater):
terrain = persiangulf.PersianGulf()
@ -758,6 +788,12 @@ class PersianGulfTheater(ConflictTheater):
"night": (0, 5),
}
@property
def projection_parameters(self) -> TransverseMercator:
from .persiangulf import PARAMETERS
return PARAMETERS
class NevadaTheater(ConflictTheater):
terrain = nevada.Nevada()
@ -774,6 +810,12 @@ class NevadaTheater(ConflictTheater):
"night": (0, 5),
}
@property
def projection_parameters(self) -> TransverseMercator:
from .nevada import PARAMETERS
return PARAMETERS
class NormandyTheater(ConflictTheater):
terrain = normandy.Normandy()
@ -790,6 +832,12 @@ class NormandyTheater(ConflictTheater):
"night": (0, 5),
}
@property
def projection_parameters(self) -> TransverseMercator:
from .normandy import PARAMETERS
return PARAMETERS
class TheChannelTheater(ConflictTheater):
terrain = thechannel.TheChannel()
@ -806,6 +854,12 @@ class TheChannelTheater(ConflictTheater):
"night": (0, 5),
}
@property
def projection_parameters(self) -> TransverseMercator:
from .thechannel import PARAMETERS
return PARAMETERS
class SyriaTheater(ConflictTheater):
terrain = syria.Syria()
@ -822,6 +876,12 @@ class SyriaTheater(ConflictTheater):
"night": (0, 5),
}
@property
def projection_parameters(self) -> TransverseMercator:
from .syria import PARAMETERS
return PARAMETERS
@dataclass
class ComplexFrontLine:

View File

@ -33,6 +33,7 @@ from dcs.mission import Mission
from dcs.unittype import FlyingType
from tabulate import tabulate
from game.theater import ConflictTheater
from game.utils import meters
from .aircraft import AIRCRAFT_DATA, FlightData
from .airsupportgen import AwacsInfo, TankerInfo
@ -293,7 +294,6 @@ class BriefingPage(KneeboardPage):
headers=["#", "Action", "Alt", "Dist", "GSPD", "Time", "Departure"],
)
flight_plan_builder
writer.table(
[
[
@ -415,6 +415,41 @@ class BriefingPage(KneeboardPage):
return local_time.strftime(f"%H:%M:%S")
class TargetInfoPage(KneeboardPage):
"""A kneeboard page containing target information."""
def __init__(
self,
flight: FlightData,
targets: List[FlightWaypoint],
dark_kneeboard: bool,
theater: ConflictTheater,
) -> None:
self.flight = flight
self.targets = targets
self.dark_kneeboard = dark_kneeboard
self.theater = theater
def write(self, path: Path) -> None:
writer = KneeboardPageWriter(dark_theme=self.dark_kneeboard)
if self.flight.custom_name is not None:
custom_name_title = ' ("{}")'.format(self.flight.custom_name)
else:
custom_name_title = ""
writer.title(f"{self.flight.callsign} Target Info{custom_name_title}")
writer.table(
[self.target_info_row(t) for t in self.targets],
headers=["Description", "Location"],
)
writer.write(path)
def target_info_row(self, target: FlightWaypoint) -> List[str]:
ll = self.theater.point_to_ll(target.position)
return [target.pretty_name, f"{ll.latitude} {ll.longitude}"]
class KneeboardGenerator(MissionInfoGenerator):
"""Creates kneeboard pages for each client flight in the mission."""
@ -456,9 +491,21 @@ class KneeboardGenerator(MissionInfoGenerator):
)
return all_flights
def generate_target_page(self, flight: FlightData) -> Optional[KneeboardPage]:
target_waypoints = [
w
for w in flight.waypoints
if w.waypoint_type == FlightWaypointType.TARGET_GROUP_LOC
]
if not target_waypoints:
return None
return TargetInfoPage(
flight, target_waypoints, self.dark_kneeboard, self.game.theater
)
def generate_flight_kneeboard(self, flight: FlightData) -> List[KneeboardPage]:
"""Returns a list of kneeboard pages for the given flight."""
return [
pages: List[KneeboardPage] = [
BriefingPage(
flight,
self.comms,
@ -469,3 +516,8 @@ class KneeboardGenerator(MissionInfoGenerator):
self.dark_kneeboard,
),
]
if (target_page := self.generate_target_page(flight)) is not None:
pages.append(target_page)
return pages