Add exclusion zone display to the new map.

https://github.com/dcs-liberation/dcs_liberation/issues/1097
This commit is contained in:
Dan Albert 2021-05-30 18:50:25 -07:00
parent 9d1060975e
commit 2218733da4
2 changed files with 105 additions and 14 deletions

View File

@ -60,6 +60,16 @@ def shapely_poly_to_leaflet_points(
return [theater.point_to_ll(Point(x, y)).as_list() for x, y in poly.exterior.coords] return [theater.point_to_ll(Point(x, y)).as_list() for x, y in poly.exterior.coords]
def shapely_to_leaflet_polys(
poly: Union[Polygon, MultiPolygon], theater: ConflictTheater
) -> list[LeafletPoly]:
if isinstance(poly, MultiPolygon):
polys = poly.geoms
else:
polys = [poly]
return [shapely_poly_to_leaflet_points(poly, theater) for poly in polys]
class ControlPointJs(QObject): class ControlPointJs(QObject):
nameChanged = Signal() nameChanged = Signal()
blueChanged = Signal() blueChanged = Signal()
@ -581,23 +591,13 @@ class ThreatZonesJs(QObject):
def radarSams(self) -> list[LeafletPoly]: def radarSams(self) -> list[LeafletPoly]:
return self._radar_sams return self._radar_sams
@staticmethod
def polys_to_leaflet(
poly: Union[Polygon, MultiPolygon], theater: ConflictTheater
) -> list[LeafletPoly]:
if isinstance(poly, MultiPolygon):
polys = poly.geoms
else:
polys = [poly]
return [shapely_poly_to_leaflet_points(poly, theater) for poly in polys]
@classmethod @classmethod
def from_zones(cls, zones: ThreatZones, theater: ConflictTheater) -> ThreatZonesJs: def from_zones(cls, zones: ThreatZones, theater: ConflictTheater) -> ThreatZonesJs:
return ThreatZonesJs( return ThreatZonesJs(
cls.polys_to_leaflet(zones.all, theater), shapely_to_leaflet_polys(zones.all, theater),
cls.polys_to_leaflet(zones.airbases, theater), shapely_to_leaflet_polys(zones.airbases, theater),
cls.polys_to_leaflet(zones.air_defenses, theater), shapely_to_leaflet_polys(zones.air_defenses, theater),
cls.polys_to_leaflet(zones.radar_sam_threats, theater), shapely_to_leaflet_polys(zones.radar_sam_threats, theater),
) )
@classmethod @classmethod
@ -658,6 +658,44 @@ class NavMeshJs(QObject):
) )
class MapZonesJs(QObject):
inclusionZonesChanged = Signal()
exclusionZonesChanged = Signal()
seaZonesChanged = Signal()
def __init__(
self,
inclusion_zones: list[LeafletPoly],
exclusion_zones: list[LeafletPoly],
sea_zones: list[LeafletPoly],
) -> None:
super().__init__()
self._inclusion_zones = inclusion_zones
self._exclusion_zones = exclusion_zones
self._sea_zones = sea_zones
@Property(list, notify=inclusionZonesChanged)
def inclusionZones(self) -> list[LeafletPoly]:
return self._inclusion_zones
@Property(list, notify=exclusionZonesChanged)
def exclusionZones(self) -> list[LeafletPoly]:
return self._exclusion_zones
@Property(list, notify=seaZonesChanged)
def seaZones(self) -> list[LeafletPoly]:
return self._sea_zones
@classmethod
def from_game(cls, game: Game) -> MapZonesJs:
zones = game.theater.landmap
return MapZonesJs(
shapely_to_leaflet_polys(zones.inclusion_zones, game.theater),
shapely_to_leaflet_polys(zones.exclusion_zones, game.theater),
shapely_to_leaflet_polys(zones.sea_zones, game.theater),
)
class MapModel(QObject): class MapModel(QObject):
cleared = Signal() cleared = Signal()
@ -669,6 +707,7 @@ class MapModel(QObject):
frontLinesChanged = Signal() frontLinesChanged = Signal()
threatZonesChanged = Signal() threatZonesChanged = Signal()
navmeshesChanged = Signal() navmeshesChanged = Signal()
mapZonesChanged = Signal()
def __init__(self, game_model: GameModel) -> None: def __init__(self, game_model: GameModel) -> None:
super().__init__() super().__init__()
@ -683,6 +722,7 @@ class MapModel(QObject):
ThreatZonesJs.empty(), ThreatZonesJs.empty() ThreatZonesJs.empty(), ThreatZonesJs.empty()
) )
self._navmeshes = NavMeshJs([], []) self._navmeshes = NavMeshJs([], [])
self._map_zones = MapZonesJs([], [], [])
self._selected_flight_index: Optional[Tuple[int, int]] = None self._selected_flight_index: Optional[Tuple[int, int]] = None
GameUpdateSignal.get_instance().game_loaded.connect(self.on_game_load) GameUpdateSignal.get_instance().game_loaded.connect(self.on_game_load)
GameUpdateSignal.get_instance().flight_paths_changed.connect(self.reset_atos) GameUpdateSignal.get_instance().flight_paths_changed.connect(self.reset_atos)
@ -704,6 +744,7 @@ class MapModel(QObject):
ThreatZonesJs.empty(), ThreatZonesJs.empty() ThreatZonesJs.empty(), ThreatZonesJs.empty()
) )
self._navmeshes = NavMeshJs([], []) self._navmeshes = NavMeshJs([], [])
self._map_zones = MapZonesJs([], [], [])
self.cleared.emit() self.cleared.emit()
def set_package_selection(self, index: int) -> None: def set_package_selection(self, index: int) -> None:
@ -749,6 +790,7 @@ class MapModel(QObject):
self.reset_front_lines() self.reset_front_lines()
self.reset_threat_zones() self.reset_threat_zones()
self.reset_navmeshes() self.reset_navmeshes()
self.reset_map_zones()
def on_game_load(self, game: Optional[Game]) -> None: def on_game_load(self, game: Optional[Game]) -> None:
if game is not None: if game is not None:
@ -895,6 +937,14 @@ class MapModel(QObject):
def navmeshes(self) -> NavMeshJs: def navmeshes(self) -> NavMeshJs:
return self._navmeshes return self._navmeshes
def reset_map_zones(self) -> None:
self._map_zones = MapZonesJs.from_game(self.game)
self.mapZonesChanged.emit()
@Property(MapZonesJs, notify=mapZonesChanged)
def mapZones(self) -> NavMeshJs:
return self._map_zones
@property @property
def game(self) -> Game: def game(self) -> Game:
if self.game_model.game is None: if self.game_model.game is None:

View File

@ -179,6 +179,10 @@ const redRadarSamThreatZones = L.layerGroup();
const blueNavmesh = L.layerGroup(); const blueNavmesh = L.layerGroup();
const redNavmesh = L.layerGroup(); const redNavmesh = L.layerGroup();
const inclusionZones = L.layerGroup();
const exclusionZones = L.layerGroup();
const seaZones = L.layerGroup();
// Main map controls. These are the ones that we expect users to interact with. // Main map controls. These are the ones that we expect users to interact with.
// These are always open, which unfortunately means that the scroll bar will not // These are always open, which unfortunately means that the scroll bar will not
// appear if the menu doesn't fit. This fits in the smallest window size we // appear if the menu doesn't fit. This fits in the smallest window size we
@ -246,6 +250,11 @@ L.control
Blue: blueNavmesh, Blue: blueNavmesh,
Red: redNavmesh, Red: redNavmesh,
}, },
"Map Zones": {
"Inclusion zones": inclusionZones,
"Exclusion zones": exclusionZones,
"Sea zones": seaZones,
}
}, },
{ {
position: "topleft", position: "topleft",
@ -268,6 +277,7 @@ new QWebChannel(qt.webChannelTransport, function (channel) {
game.flightsChanged.connect(drawFlightPlans); game.flightsChanged.connect(drawFlightPlans);
game.threatZonesChanged.connect(drawThreatZones); game.threatZonesChanged.connect(drawThreatZones);
game.navmeshesChanged.connect(drawNavmeshes); game.navmeshesChanged.connect(drawNavmeshes);
game.mapZonesChanged.connect(drawMapZones);
}); });
function recenterMap(center) { function recenterMap(center) {
@ -889,6 +899,36 @@ function drawNavmeshes() {
drawNavmesh(game.navmeshes.red, redNavmesh); drawNavmesh(game.navmeshes.red, redNavmesh);
} }
function drawMapZones() {
seaZones.clearLayers()
inclusionZones.clearLayers();
exclusionZones.clearLayers();
for (const zone of game.mapZones.seaZones) {
L.polygon(zone, {
color: "#344455",
fillColor: "#344455",
fillOpacity: 1,
}).addTo(seaZones);
}
for (const zone of game.mapZones.inclusionZones) {
L.polygon(zone, {
color: "#969696",
fillColor: "#4b4b4b",
fillOpacity: 1,
}).addTo(inclusionZones);
}
for (const zone of game.mapZones.exclusionZones) {
L.polygon(zone, {
color: "#969696",
fillColor: "#303030",
fillOpacity: 1,
}).addTo(exclusionZones);
}
}
function drawInitialMap() { function drawInitialMap() {
recenterMap(game.mapCenter); recenterMap(game.mapCenter);
drawControlPoints(); drawControlPoints();
@ -898,6 +938,7 @@ function drawInitialMap() {
drawFlightPlans(); drawFlightPlans();
drawThreatZones(); drawThreatZones();
drawNavmeshes(); drawNavmeshes();
drawMapZones();
} }
function clearAllLayers() { function clearAllLayers() {