Improve IP selection near threat zone centers.

Fixes https://github.com/dcs-liberation/dcs_liberation/issues/2754.
This commit is contained in:
Dan Albert 2023-07-11 21:55:40 -07:00 committed by Raffson
parent f89ac52bf3
commit 12cdb8646c
No known key found for this signature in database
GPG Key ID: B0402B2C9B764D99
5 changed files with 89 additions and 8 deletions

View File

@ -209,6 +209,8 @@ BAI/ANTISHIP/DEAD/STRIKE/BARCAP/CAS/OCA/AIR-ASSAULT (main) missions
## Features/Improvements
* **[Engine]** Support for DCS 2.8.6.41363, including F-15E support.
* **[Flight Planning]** Improved IP selection for targets that are near the center of a threat zone.
* **[Modding]** Factions can now specify the ship type to be used for cargo shipping. The Handy Wind will be used by default, but WW2 factions can pick something more appropriate.
## Fixes

View File

@ -384,6 +384,8 @@ export type IpZones = {
ipBubble: LatLng[][];
permissibleZone: LatLng[][];
safeZones: LatLng[][][];
preferredThreatenedZones: LatLng[][][];
tolerableThreatenedLines: LatLng[][];
};
export type JoinZones = {
homeBubble: LatLng[][];

View File

@ -1,5 +1,5 @@
import { useGetDebugIpZonesQuery } from "../../api/liberationApi";
import { LayerGroup, Polygon } from "react-leaflet";
import { LayerGroup, Polygon, Polyline } from "react-leaflet";
interface IpZonesProps {
flightId: string;
@ -56,6 +56,29 @@ function IpZones(props: IpZonesProps) {
/>
);
})}
{data.preferredThreatenedZones.map((zone, idx) => {
return (
<Polygon
key={idx}
positions={zone}
color="#fe7d0a"
fillOpacity={0.1}
interactive={false}
/>
);
})}
{data.tolerableThreatenedLines.map((line, idx) => {
return (
<Polyline
key={idx}
positions={line}
color="#80BA80"
interactive={false}
/>
);
})}
</>
);
}

View File

@ -4,7 +4,7 @@ from typing import TYPE_CHECKING
import shapely.ops
from dcs import Point
from shapely.geometry import MultiPolygon, Point as ShapelyPoint
from shapely.geometry import MultiPolygon, Point as ShapelyPoint, MultiLineString
from game.utils import meters, nautical_miles
@ -90,6 +90,32 @@ class IpZoneGeometry:
safe_zones = MultiPolygon([safe_zones])
self.safe_zones = safe_zones
# See explanation where this is used in _unsafe_ip.
# https://github.com/dcs-liberation/dcs_liberation/issues/2754
preferred_threatened_zone_wiggle_room = nautical_miles(5)
threat_buffer_distance = self.permissible_zone.distance(
self.threat_zone.boundary
)
preferred_threatened_zone_mask = self.threat_zone.buffer(
-threat_buffer_distance - preferred_threatened_zone_wiggle_room.meters
)
preferred_threatened_zones = self.threat_zone.difference(
preferred_threatened_zone_mask
)
if not isinstance(preferred_threatened_zones, MultiPolygon):
preferred_threatened_zones = MultiPolygon([preferred_threatened_zones])
self.preferred_threatened_zones = preferred_threatened_zones
tolerable_threatened_lines = self.preferred_threatened_zones.intersection(
self.permissible_zone.boundary
)
if tolerable_threatened_lines.is_empty:
tolerable_threatened_lines = MultiLineString([])
elif not isinstance(tolerable_threatened_lines, MultiLineString):
tolerable_threatened_lines = MultiLineString([tolerable_threatened_lines])
self.tolerable_threatened_lines = tolerable_threatened_lines
def _unsafe_ip(self) -> ShapelyPoint:
unthreatened_home_zone = self.home_bubble.difference(self.threat_zone)
if unthreatened_home_zone.is_empty:
@ -100,8 +126,28 @@ class IpZoneGeometry:
self.permissible_zone, self.threat_zone.boundary
)[0]
# No safe point in the IP zone, but the home zone is safe. Pick the max-
# distance IP that's closest to the untreatened home zone.
# No safe point in the IP zone, but the home zone is safe. Pick an IP within
# both the permissible zone and preferred threatened zone that's as close to the
# unthreatened home zone as possible. This should get us a max-range IP that
# is roughly as safe as possible without unjustifiably long routes.
#
# If we do the obvious thing and pick the IP that minimizes threatened travel
# time (the IP closest to the threat boundary) and the objective is near the
# center of the threat zone (common when there is an airbase covered only by air
# defenses with shorter range than the BARCAP zone, and the target is a TGO near
# the CP), the IP could be placed such that the flight would fly all the way
# around the threat zone just to avoid a few more threatened miles of travel. To
# avoid that, we generate a set of preferred threatened areas that offer a
# trade-off between travel time and safety.
#
# https://github.com/dcs-liberation/dcs_liberation/issues/2754
if not self.tolerable_threatened_lines.is_empty:
return shapely.ops.nearest_points(
self.tolerable_threatened_lines, self.home
)[0]
# But if no part of the permissible zone is tolerably threatened, fall back to
# the old safety maximizing approach.
return shapely.ops.nearest_points(
self.permissible_zone, unthreatened_home_zone
)[0]

View File

@ -64,14 +64,16 @@ class IpZonesJs(BaseModel):
ipBubble: LeafletPoly = Field(alias="ipBubble")
permissibleZone: LeafletPoly = Field(alias="permissibleZone")
safeZones: list[LeafletPoly] = Field(alias="safeZones")
preferred_threatened_zones: list[LeafletPoly] = Field(
alias="preferredThreatenedZones"
)
tolerable_threatened_lines: list[LeafletLine] = Field(
alias="tolerableThreatenedLines"
)
class Config:
title = "IpZones"
@classmethod
def empty(cls) -> IpZonesJs:
return IpZonesJs(homeBubble=[], ipBubble=[], permissibleZone=[], safeZones=[])
@classmethod
def for_flight(cls, flight: Flight, game: Game) -> IpZonesJs:
target = flight.package.target
@ -84,6 +86,12 @@ class IpZonesJs(BaseModel):
geometry.permissible_zone, game.theater
),
safeZones=ShapelyUtil.polys_to_leaflet(geometry.safe_zones, game.theater),
preferredThreatenedZones=ShapelyUtil.polys_to_leaflet(
geometry.preferred_threatened_zones, game.theater
),
tolerableThreatenedLines=ShapelyUtil.lines_to_leaflet(
geometry.tolerable_threatened_lines, game.theater
),
)