dcs_liberation/game/flightplan/waypointsolverloader.py
zhexu14 4631ee0d74
Doctrine load from YAML (#3291)
This PR refactors the Doctrine class to load from YAML files in the
resources folder instead of being hardcoded as a step towards making
doctrines moddable (Issue #829).

I haven't added anything to the changelog as a couple of things should
get cleaned up first:
- As far as I can tell, the flags in the Doctrine class (cap, cas, sead
etc.) aren't used anywhere. Need to test further, and if they're truly
not used, will remove them.
- Probably need to update the Wiki
2023-12-17 18:42:31 -08:00

77 lines
2.6 KiB
Python

import json
from functools import cached_property
from pathlib import Path
from typing import Any
from dcs.mapping import Point as DcsPoint, LatLng
from dcs.terrain import Terrain
from numpy import float64, array
from numpy._typing import NDArray
from shapely import transform
from shapely.geometry import shape
from shapely.geometry.base import BaseGeometry
from game.data.doctrine import Doctrine
from .ipsolver import IpSolver
from .waypointsolver import WaypointSolver
from ..theater.theaterloader import TERRAINS_BY_NAME
def doctrine_from_name(name: str) -> Doctrine:
return Doctrine.named(name)
def geometry_ll_to_xy(geometry: BaseGeometry, terrain: Terrain) -> BaseGeometry:
if geometry.is_empty:
return geometry
def ll_to_xy(points: NDArray[float64]) -> NDArray[float64]:
ll_points = []
for point in points:
# Longitude is unintuitively first because it's the "X" coordinate:
# https://datatracker.ietf.org/doc/html/rfc7946#section-3.1.1
p = DcsPoint.from_latlng(LatLng(point[1], point[0]), terrain)
ll_points.append([p.x, p.y])
return array(ll_points)
return transform(geometry, ll_to_xy)
class WaypointSolverLoader:
def __init__(self, debug_info_path: Path) -> None:
self.debug_info_path = debug_info_path
def load_data(self) -> dict[str, Any]:
with self.debug_info_path.open(encoding="utf-8") as debug_info_file:
return json.load(debug_info_file)
@staticmethod
def load_geometries(
feature_collection: dict[str, Any], terrain: Terrain
) -> dict[str, BaseGeometry]:
geometries = {}
for feature in feature_collection["features"]:
description = feature["properties"]["description"]
geometry = shape(feature["geometry"])
geometries[description] = geometry_ll_to_xy(geometry, terrain)
return geometries
@cached_property
def terrain(self) -> Terrain:
return TERRAINS_BY_NAME[self.load_data()["metadata"]["terrain"]]
def load(self) -> WaypointSolver:
data = self.load_data()
metadata = data["metadata"]
name = metadata.pop("name")
terrain_name = metadata.pop("terrain")
terrain = TERRAINS_BY_NAME[terrain_name]
if "doctrine" in metadata:
metadata["doctrine"] = doctrine_from_name(metadata["doctrine"])
geometries = self.load_geometries(data, terrain)
builder: type[WaypointSolver] = {
"IpSolver": IpSolver,
}[name]
metadata.update(geometries)
return builder(**metadata)