diff --git a/game/theater/conflicttheater.py b/game/theater/conflicttheater.py index f9c928ca..12ac9411 100644 --- a/game/theater/conflicttheater.py +++ b/game/theater/conflicttheater.py @@ -476,6 +476,9 @@ class LatLon: latitude: float longitude: float + def as_list(self) -> List[float]: + return [self.latitude, self.longitude] + class ConflictTheater: terrain: Terrain diff --git a/qt_ui/widgets/map/mapmodel.py b/qt_ui/widgets/map/mapmodel.py index 1785d55d..bffc61fd 100644 --- a/qt_ui/widgets/map/mapmodel.py +++ b/qt_ui/widgets/map/mapmodel.py @@ -6,8 +6,10 @@ from PySide2.QtCore import Property, QObject, Signal, Slot from dcs import Point from dcs.unit import Unit from dcs.vehicles import vehicle_map +from shapely.geometry import LineString, Point as ShapelyPoint, Polygon from game import Game, db +from game.factions.faction import Faction from game.profiling import logged_duration from game.theater import ( ConflictTheater, @@ -18,7 +20,7 @@ from game.theater import ( from game.utils import meters, nautical_miles from gen.ato import AirTaskingOrder from gen.flights.flight import Flight, FlightWaypoint, FlightWaypointType -from gen.flights.flightplan import FlightPlan +from gen.flights.flightplan import FlightPlan, PatrollingFlightPlan from qt_ui.dialogs import Dialog from qt_ui.models import GameModel from qt_ui.windows.GameUpdateSignal import GameUpdateSignal @@ -43,6 +45,14 @@ LeafletLatLon = List[float] # needs a named signal for every property, even if it is constant. +def shapely_poly_to_leaflet_points( + poly: Polygon, theater: ConflictTheater +) -> Optional[List[LeafletLatLon]]: + if poly.is_empty: + return None + return [theater.point_to_ll(Point(x, y)).as_list() for x, y in poly.exterior.coords] + + class ControlPointJs(QObject): nameChanged = Signal() blueChanged = Signal() @@ -312,18 +322,20 @@ class FlightJs(QObject): flightPlanChanged = Signal() blueChanged = Signal() selectedChanged = Signal() + commitBoundaryChanged = Signal() def __init__( - self, flight: Flight, selected: bool, theater: ConflictTheater + self, flight: Flight, selected: bool, theater: ConflictTheater, faction: Faction ) -> None: super().__init__() self.flight = flight self._selected = selected self.theater = theater - self._waypoints = [] - self.reset_waypoints() + self.faction = faction + self._waypoints = self.make_waypoints() + self._commit_boundary = self.make_commit_boundary() - def reset_waypoints(self) -> None: + def make_waypoints(self) -> List[WaypointJs]: departure = FlightWaypoint( FlightWaypointType.TAKEOFF, self.flight.departure.position.x, @@ -331,11 +343,25 @@ class FlightJs(QObject): meters(0), ) departure.alt_type = "RADIO" - self._waypoints = [ + return [ WaypointJs(p, i, self.flight.flight_plan, self.theater) for i, p in enumerate([departure] + self.flight.points) ] - self.flightPlanChanged.emit() + + def make_commit_boundary(self) -> Optional[List[LeafletLatLon]]: + if not isinstance(self.flight.flight_plan, PatrollingFlightPlan): + return [] + start = self.flight.flight_plan.patrol_start + end = self.flight.flight_plan.patrol_end + line = LineString( + [ + ShapelyPoint(start.x, start.y), + ShapelyPoint(end.x, end.y), + ] + ) + doctrine = self.faction.doctrine + bubble = line.buffer(doctrine.cap_engagement_range.meters) + return shapely_poly_to_leaflet_points(bubble, self.theater) @Property(list, notify=flightPlanChanged) def flightPlan(self) -> List[WaypointJs]: @@ -349,6 +375,10 @@ class FlightJs(QObject): def selected(self) -> bool: return self._selected + @Property(list) + def commitBoundary(self) -> Optional[List[LeafletLatLon]]: + return self._commit_boundary + class MapModel(QObject): cleared = Signal() @@ -452,6 +482,7 @@ class MapModel(QObject): flight, selected=blue and (p_idx, f_idx) == self._selected_flight_index, theater=self.game.theater, + faction=self.game.faction_for(blue), ) ) return flights diff --git a/resources/ui/map/map.js b/resources/ui/map/map.js index 47b0206d..4a4098f5 100644 --- a/resources/ui/map/map.js +++ b/resources/ui/map/map.js @@ -7,7 +7,6 @@ * - CV waypoints * - Time of day/weather themeing * - Exclusion zones - * - Commit ranges * - Supply route status * - "Actual" front line * - Debug flight plan drawing @@ -247,8 +246,14 @@ function drawFlightPlan(flight) { }); if (flight.selected) { - L.polyline(points, { color: highlight }).addTo(selectedFlightPlansLayer); - L.polyline(points, { color: highlight }).addTo(layer); + L.polyline(points, { color: highlight }) + .addTo(layer) + .addTo(selectedFlightPlansLayer); + if (flight.commitBoundary) { + L.polyline(flight.commitBoundary, { color: highlight, weight: 1 }).addTo( + layer.addTo(selectedFlightPlansLayer) + ); + } } else { L.polyline(points, { color: color, weight: 1 }).addTo(layer); }