mirror of
https://github.com/dcs-liberation/dcs_liberation.git
synced 2025-11-10 14:22:26 +00:00
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:
parent
8a01209ded
commit
2a06a1ffdf
@ -40,6 +40,7 @@ from dcs.unitgroup import (
|
|||||||
VehicleGroup,
|
VehicleGroup,
|
||||||
)
|
)
|
||||||
from dcs.vehicles import AirDefence, Armor, MissilesSS, Unarmed
|
from dcs.vehicles import AirDefence, Armor, MissilesSS, Unarmed
|
||||||
|
from pyproj import CRS, Transformer
|
||||||
from shapely import geometry, ops
|
from shapely import geometry, ops
|
||||||
|
|
||||||
from gen.flights.flight import FlightType
|
from gen.flights.flight import FlightType
|
||||||
@ -53,6 +54,7 @@ from .controlpoint import (
|
|||||||
OffMapSpawn,
|
OffMapSpawn,
|
||||||
)
|
)
|
||||||
from .landmap import Landmap, load_landmap, poly_contains
|
from .landmap import Landmap, load_landmap, poly_contains
|
||||||
|
from .projections import TransverseMercator
|
||||||
from ..point_with_heading import PointWithHeading
|
from ..point_with_heading import PointWithHeading
|
||||||
from ..utils import Distance, meters, nautical_miles, pairwise
|
from ..utils import Distance, meters, nautical_miles, pairwise
|
||||||
|
|
||||||
@ -473,6 +475,12 @@ class ReferencePoint:
|
|||||||
image_coordinates: Point
|
image_coordinates: Point
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(frozen=True)
|
||||||
|
class LatLon:
|
||||||
|
latitude: float
|
||||||
|
longitude: float
|
||||||
|
|
||||||
|
|
||||||
class ConflictTheater:
|
class ConflictTheater:
|
||||||
terrain: Terrain
|
terrain: Terrain
|
||||||
|
|
||||||
@ -725,6 +733,22 @@ class ConflictTheater:
|
|||||||
MizCampaignLoader(directory / miz, t).populate_theater()
|
MizCampaignLoader(directory / miz, t).populate_theater()
|
||||||
return t
|
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):
|
class CaucasusTheater(ConflictTheater):
|
||||||
terrain = caucasus.Caucasus()
|
terrain = caucasus.Caucasus()
|
||||||
@ -742,6 +766,12 @@ class CaucasusTheater(ConflictTheater):
|
|||||||
"night": (0, 5),
|
"night": (0, 5),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@property
|
||||||
|
def projection_parameters(self) -> TransverseMercator:
|
||||||
|
from .caucasus import PARAMETERS
|
||||||
|
|
||||||
|
return PARAMETERS
|
||||||
|
|
||||||
|
|
||||||
class PersianGulfTheater(ConflictTheater):
|
class PersianGulfTheater(ConflictTheater):
|
||||||
terrain = persiangulf.PersianGulf()
|
terrain = persiangulf.PersianGulf()
|
||||||
@ -758,6 +788,12 @@ class PersianGulfTheater(ConflictTheater):
|
|||||||
"night": (0, 5),
|
"night": (0, 5),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@property
|
||||||
|
def projection_parameters(self) -> TransverseMercator:
|
||||||
|
from .persiangulf import PARAMETERS
|
||||||
|
|
||||||
|
return PARAMETERS
|
||||||
|
|
||||||
|
|
||||||
class NevadaTheater(ConflictTheater):
|
class NevadaTheater(ConflictTheater):
|
||||||
terrain = nevada.Nevada()
|
terrain = nevada.Nevada()
|
||||||
@ -774,6 +810,12 @@ class NevadaTheater(ConflictTheater):
|
|||||||
"night": (0, 5),
|
"night": (0, 5),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@property
|
||||||
|
def projection_parameters(self) -> TransverseMercator:
|
||||||
|
from .nevada import PARAMETERS
|
||||||
|
|
||||||
|
return PARAMETERS
|
||||||
|
|
||||||
|
|
||||||
class NormandyTheater(ConflictTheater):
|
class NormandyTheater(ConflictTheater):
|
||||||
terrain = normandy.Normandy()
|
terrain = normandy.Normandy()
|
||||||
@ -790,6 +832,12 @@ class NormandyTheater(ConflictTheater):
|
|||||||
"night": (0, 5),
|
"night": (0, 5),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@property
|
||||||
|
def projection_parameters(self) -> TransverseMercator:
|
||||||
|
from .normandy import PARAMETERS
|
||||||
|
|
||||||
|
return PARAMETERS
|
||||||
|
|
||||||
|
|
||||||
class TheChannelTheater(ConflictTheater):
|
class TheChannelTheater(ConflictTheater):
|
||||||
terrain = thechannel.TheChannel()
|
terrain = thechannel.TheChannel()
|
||||||
@ -806,6 +854,12 @@ class TheChannelTheater(ConflictTheater):
|
|||||||
"night": (0, 5),
|
"night": (0, 5),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@property
|
||||||
|
def projection_parameters(self) -> TransverseMercator:
|
||||||
|
from .thechannel import PARAMETERS
|
||||||
|
|
||||||
|
return PARAMETERS
|
||||||
|
|
||||||
|
|
||||||
class SyriaTheater(ConflictTheater):
|
class SyriaTheater(ConflictTheater):
|
||||||
terrain = syria.Syria()
|
terrain = syria.Syria()
|
||||||
@ -822,6 +876,12 @@ class SyriaTheater(ConflictTheater):
|
|||||||
"night": (0, 5),
|
"night": (0, 5),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@property
|
||||||
|
def projection_parameters(self) -> TransverseMercator:
|
||||||
|
from .syria import PARAMETERS
|
||||||
|
|
||||||
|
return PARAMETERS
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class ComplexFrontLine:
|
class ComplexFrontLine:
|
||||||
|
|||||||
@ -33,6 +33,7 @@ from dcs.mission import Mission
|
|||||||
from dcs.unittype import FlyingType
|
from dcs.unittype import FlyingType
|
||||||
from tabulate import tabulate
|
from tabulate import tabulate
|
||||||
|
|
||||||
|
from game.theater import ConflictTheater
|
||||||
from game.utils import meters
|
from game.utils import meters
|
||||||
from .aircraft import AIRCRAFT_DATA, FlightData
|
from .aircraft import AIRCRAFT_DATA, FlightData
|
||||||
from .airsupportgen import AwacsInfo, TankerInfo
|
from .airsupportgen import AwacsInfo, TankerInfo
|
||||||
@ -293,7 +294,6 @@ class BriefingPage(KneeboardPage):
|
|||||||
headers=["#", "Action", "Alt", "Dist", "GSPD", "Time", "Departure"],
|
headers=["#", "Action", "Alt", "Dist", "GSPD", "Time", "Departure"],
|
||||||
)
|
)
|
||||||
|
|
||||||
flight_plan_builder
|
|
||||||
writer.table(
|
writer.table(
|
||||||
[
|
[
|
||||||
[
|
[
|
||||||
@ -415,6 +415,41 @@ class BriefingPage(KneeboardPage):
|
|||||||
return local_time.strftime(f"%H:%M:%S")
|
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):
|
class KneeboardGenerator(MissionInfoGenerator):
|
||||||
"""Creates kneeboard pages for each client flight in the mission."""
|
"""Creates kneeboard pages for each client flight in the mission."""
|
||||||
|
|
||||||
@ -456,9 +491,21 @@ class KneeboardGenerator(MissionInfoGenerator):
|
|||||||
)
|
)
|
||||||
return all_flights
|
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]:
|
def generate_flight_kneeboard(self, flight: FlightData) -> List[KneeboardPage]:
|
||||||
"""Returns a list of kneeboard pages for the given flight."""
|
"""Returns a list of kneeboard pages for the given flight."""
|
||||||
return [
|
pages: List[KneeboardPage] = [
|
||||||
BriefingPage(
|
BriefingPage(
|
||||||
flight,
|
flight,
|
||||||
self.comms,
|
self.comms,
|
||||||
@ -469,3 +516,8 @@ class KneeboardGenerator(MissionInfoGenerator):
|
|||||||
self.dark_kneeboard,
|
self.dark_kneeboard,
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
if (target_page := self.generate_target_page(flight)) is not None:
|
||||||
|
pages.append(target_page)
|
||||||
|
|
||||||
|
return pages
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user