From e22e8669e18194b4a3246120f50bd860f96d2d53 Mon Sep 17 00:00:00 2001 From: Dan Albert Date: Sun, 18 Jul 2021 14:41:22 -0700 Subject: [PATCH] Add fallback locations for join zones. It's rare with the current 5NM buffer around the origin, but if we use the hold distance as the buffer like we maybe should it's possible for the preferred join locations to fall entirely within the home zone. In that case, fall back to a location within the max-turn-zone that's outside the home zone and is nearest the IP. --- game/flightplan/joinzonegeometry.py | 32 +++++++++++++--------- gen/flights/flightplan.py | 1 - qt_ui/widgets/map/mapmodel.py | 42 ++++++++++++++++------------- resources/ui/map/map.js | 16 ++++++++++- 4 files changed, 59 insertions(+), 32 deletions(-) diff --git a/game/flightplan/joinzonegeometry.py b/game/flightplan/joinzonegeometry.py index 48cff780..02e00fa4 100644 --- a/game/flightplan/joinzonegeometry.py +++ b/game/flightplan/joinzonegeometry.py @@ -11,7 +11,6 @@ from shapely.geometry import ( MultiLineString, ) -from game.theater import ConflictTheater from game.utils import nautical_miles if TYPE_CHECKING: @@ -26,12 +25,7 @@ class JoinZoneGeometry: """ def __init__( - self, - target: Point, - home: Point, - ip: Point, - coalition: Coalition, - theater: ConflictTheater, + self, target: Point, home: Point, ip: Point, coalition: Coalition ) -> None: # Normal join placement is based on the path from home to the IP. If no path is # found it means that the target is on a direct path. In that case we instead @@ -82,14 +76,28 @@ class JoinZoneGeometry: ] ) - permissible_lines = ip_direction_limit_wedge.intersection( + permissible_zones = ip_direction_limit_wedge.difference( + self.excluded_zones + ).difference(self.home_bubble) + if permissible_zones.is_empty: + permissible_zones = MultiPolygon([]) + if not isinstance(permissible_zones, MultiPolygon): + permissible_zones = MultiPolygon([permissible_zones]) + self.permissible_zones = permissible_zones + + preferred_lines = ip_direction_limit_wedge.intersection( self.excluded_zones.boundary ).difference(self.home_bubble) - if not isinstance(permissible_lines, MultiLineString): - permissible_lines = MultiLineString([permissible_lines]) - self.permissible_lines = permissible_lines + if preferred_lines.is_empty: + preferred_lines = MultiLineString([]) + if not isinstance(preferred_lines, MultiLineString): + preferred_lines = MultiLineString([preferred_lines]) + self.preferred_lines = preferred_lines def find_best_join_point(self) -> Point: - join, _ = shapely.ops.nearest_points(self.permissible_lines, self.home) + if self.preferred_lines.is_empty: + join, _ = shapely.ops.nearest_points(self.permissible_zones, self.ip) + else: + join, _ = shapely.ops.nearest_points(self.preferred_lines, self.home) return Point(join.x, join.y) diff --git a/gen/flights/flightplan.py b/gen/flights/flightplan.py index 6c25babd..fde43ee9 100644 --- a/gen/flights/flightplan.py +++ b/gen/flights/flightplan.py @@ -975,7 +975,6 @@ class FlightPlanBuilder: package_airfield.position, ingress_point, self.coalition, - self.theater, ).find_best_join_point() # And the split point based on the best route from the IP. Since that's no diff --git a/qt_ui/widgets/map/mapmodel.py b/qt_ui/widgets/map/mapmodel.py index e8a75298..fce7a5d9 100644 --- a/qt_ui/widgets/map/mapmodel.py +++ b/qt_ui/widgets/map/mapmodel.py @@ -869,7 +869,8 @@ class JoinZonesJs(QObject): targetBubbleChanged = Signal() ipBubbleChanged = Signal() excludedZonesChanged = Signal() - permissibleLinesChanged = Signal() + permissibleZonesChanged = Signal() + preferredLinesChanged = Signal() def __init__( self, @@ -877,14 +878,16 @@ class JoinZonesJs(QObject): target_bubble: LeafletPoly, ip_bubble: LeafletPoly, excluded_zones: list[LeafletPoly], - permissible_lines: list[list[LeafletLatLon]], + permissible_zones: list[LeafletPoly], + preferred_lines: list[list[LeafletLatLon]], ) -> None: super().__init__() self._home_bubble = home_bubble self._target_bubble = target_bubble self._ip_bubble = ip_bubble self._excluded_zones = excluded_zones - self._permissible_lines = permissible_lines + self._permissible_zones = permissible_zones + self._preferred_lines = preferred_lines @Property(list, notify=homeBubbleChanged) def homeBubble(self) -> LeafletPoly: @@ -902,13 +905,17 @@ class JoinZonesJs(QObject): def excludedZones(self) -> list[LeafletPoly]: return self._excluded_zones - @Property(list, notify=permissibleLinesChanged) - def permissibleLines(self) -> list[list[LeafletLatLon]]: - return self._permissible_lines + @Property(list, notify=permissibleZonesChanged) + def permissibleZones(self) -> list[LeafletPoly]: + return self._permissible_zones + + @Property(list, notify=preferredLinesChanged) + def preferredLines(self) -> list[list[LeafletLatLon]]: + return self._preferred_lines @classmethod def empty(cls) -> JoinZonesJs: - return JoinZonesJs([], [], [], [], []) + return JoinZonesJs([], [], [], [], [], []) @classmethod def for_flight(cls, flight: Flight, game: Game) -> JoinZonesJs: @@ -919,15 +926,14 @@ class JoinZonesJs(QObject): if flight.package.waypoints is None: return JoinZonesJs.empty() ip = flight.package.waypoints.ingress - geometry = JoinZoneGeometry( - target.position, home.position, ip, game.blue, game.theater - ) + geometry = JoinZoneGeometry(target.position, home.position, ip, game.blue) return JoinZonesJs( shapely_poly_to_leaflet_points(geometry.home_bubble, game.theater), shapely_poly_to_leaflet_points(geometry.target_bubble, game.theater), shapely_poly_to_leaflet_points(geometry.ip_bubble, game.theater), shapely_to_leaflet_polys(geometry.excluded_zones, game.theater), - shapely_lines_to_leaflet_points(geometry.permissible_lines, game.theater), + shapely_to_leaflet_polys(geometry.permissible_zones, game.theater), + shapely_lines_to_leaflet_points(geometry.preferred_lines, game.theater), ) @@ -937,7 +943,7 @@ class HoldZonesJs(QObject): joinBubbleChanged = Signal() excludedZonesChanged = Signal() permissibleZonesChanged = Signal() - permissibleLinesChanged = Signal() + preferredLinesChanged = Signal() def __init__( self, @@ -946,7 +952,7 @@ class HoldZonesJs(QObject): join_bubble: LeafletPoly, excluded_zones: list[LeafletPoly], permissible_zones: list[LeafletPoly], - permissible_lines: list[list[LeafletLatLon]], + preferred_lines: list[list[LeafletLatLon]], ) -> None: super().__init__() self._home_bubble = home_bubble @@ -954,7 +960,7 @@ class HoldZonesJs(QObject): self._join_bubble = join_bubble self._excluded_zones = excluded_zones self._permissible_zones = permissible_zones - self._permissible_lines = permissible_lines + self._preferred_lines = preferred_lines @Property(list, notify=homeBubbleChanged) def homeBubble(self) -> LeafletPoly: @@ -976,9 +982,9 @@ class HoldZonesJs(QObject): def permissibleZones(self) -> list[LeafletPoly]: return self._permissible_zones - @Property(list, notify=permissibleLinesChanged) - def permissibleLines(self) -> list[list[LeafletLatLon]]: - return self._permissible_lines + @Property(list, notify=preferredLinesChanged) + def preferredLines(self) -> list[list[LeafletLatLon]]: + return self._preferred_lines @classmethod def empty(cls) -> HoldZonesJs: @@ -1003,7 +1009,7 @@ class HoldZonesJs(QObject): shapely_poly_to_leaflet_points(geometry.join_bubble, game.theater), shapely_to_leaflet_polys(geometry.excluded_zones, game.theater), shapely_to_leaflet_polys(geometry.permissible_zones, game.theater), - [], # shapely_to_leaflet_polys(geometry.permissible_lines, game.theater), + shapely_lines_to_leaflet_points(geometry.preferred_lines, game.theater), ) diff --git a/resources/ui/map/map.js b/resources/ui/map/map.js index 831701a9..4e4ff59e 100644 --- a/resources/ui/map/map.js +++ b/resources/ui/map/map.js @@ -1066,7 +1066,14 @@ function drawJoinZones() { }).addTo(joinZones); } - for (const line of game.joinZones.permissibleLines) { + for (const zone of game.joinZones.permissibleZones) { + L.polygon(zone, { + color: Colors.Green, + interactive: false, + }).addTo(joinZones); + } + + for (const line of game.joinZones.preferredLines) { L.polyline(line, { color: Colors.Green, interactive: false, @@ -1114,6 +1121,13 @@ function drawHoldZones() { interactive: false, }).addTo(holdZones); } + + for (const line of game.holdZones.preferredLines) { + L.polyline(line, { + color: Colors.Green, + interactive: false, + }).addTo(holdZones); + } } function drawInitialMap() {