Remove our old lat/lon support code.

pydcs provides this now.
This commit is contained in:
Dan Albert
2022-02-22 17:40:07 -08:00
parent bb72acd3ac
commit 1a9930b93a
26 changed files with 57 additions and 640 deletions

View File

@@ -41,7 +41,7 @@ from game.data.alic import AlicCodes
from game.dcs.aircrafttype import AircraftType
from game.radio.radios import RadioFrequency
from game.runways import RunwayData
from game.theater import ConflictTheater, LatLon, TheaterGroundObject, TheaterUnit
from game.theater import TheaterGroundObject, TheaterUnit
from game.theater.bullseye import Bullseye
from game.utils import Distance, UnitSystem, meters, mps, pounds
from game.weather import Weather
@@ -183,12 +183,6 @@ class KneeboardPage:
"""Writes the kneeboard page to the given path."""
raise NotImplementedError
@staticmethod
def format_ll(ll: LatLon) -> str:
ns = "N" if ll.lat >= 0 else "S"
ew = "E" if ll.lng >= 0 else "W"
return f"{ll.lat:.4}°{ns} {ll.lng:.4}°{ew}"
@dataclass(frozen=True)
class NumberedWaypoint:
@@ -325,14 +319,12 @@ class BriefingPage(KneeboardPage):
self,
flight: FlightData,
bullseye: Bullseye,
theater: ConflictTheater,
weather: Weather,
start_time: datetime.datetime,
dark_kneeboard: bool,
) -> None:
self.flight = flight
self.bullseye = bullseye
self.theater = theater
self.weather = weather
self.start_time = start_time
self.dark_kneeboard = dark_kneeboard
@@ -399,7 +391,7 @@ class BriefingPage(KneeboardPage):
font=self.flight_plan_font,
)
writer.text(f"Bullseye: {self.bullseye.to_lat_lon(self.theater).format_dms()}")
writer.text(f"Bullseye: {self.bullseye.position.latlng().format_dms()}")
qnh_in_hg = f"{self.weather.atmospheric.qnh.inches_hg:.2f}"
qnh_mm_hg = f"{self.weather.atmospheric.qnh.mm_hg:.1f}"
@@ -598,12 +590,9 @@ class SupportPage(KneeboardPage):
class SeadTaskPage(KneeboardPage):
"""A kneeboard page containing SEAD/DEAD target information."""
def __init__(
self, flight: FlightData, dark_kneeboard: bool, theater: ConflictTheater
) -> None:
def __init__(self, flight: FlightData, dark_kneeboard: bool) -> None:
self.flight = flight
self.dark_kneeboard = dark_kneeboard
self.theater = theater
@property
def target_units(self) -> Iterator[TheaterUnit]:
@@ -634,7 +623,7 @@ class SeadTaskPage(KneeboardPage):
writer.write(path)
def target_info_row(self, unit: TheaterUnit) -> List[str]:
ll = self.theater.point_to_ll(unit.position)
ll = unit.position.latlng()
unit_type = unit.type
name = unit.name if unit_type is None else unit_type.name
return [
@@ -647,15 +636,9 @@ class SeadTaskPage(KneeboardPage):
class StrikeTaskPage(KneeboardPage):
"""A kneeboard page containing strike target information."""
def __init__(
self,
flight: FlightData,
dark_kneeboard: bool,
theater: ConflictTheater,
) -> None:
def __init__(self, flight: FlightData, dark_kneeboard: bool) -> None:
self.flight = flight
self.dark_kneeboard = dark_kneeboard
self.theater = theater
@property
def targets(self) -> Iterator[NumberedWaypoint]:
@@ -678,12 +661,12 @@ class StrikeTaskPage(KneeboardPage):
writer.write(path)
def target_info_row(self, target: NumberedWaypoint) -> List[str]:
ll = self.theater.point_to_ll(target.waypoint.position)
@staticmethod
def target_info_row(target: NumberedWaypoint) -> list[str]:
return [
str(target.number),
target.waypoint.pretty_name,
ll.format_dms(include_decimal_seconds=True),
target.waypoint.position.latlng().format_dms(include_decimal_seconds=True),
]
@@ -748,9 +731,9 @@ class KneeboardGenerator(MissionInfoGenerator):
def generate_task_page(self, flight: FlightData) -> Optional[KneeboardPage]:
if flight.flight_type in (FlightType.DEAD, FlightType.SEAD):
return SeadTaskPage(flight, self.dark_kneeboard, self.game.theater)
return SeadTaskPage(flight, self.dark_kneeboard)
elif flight.flight_type is FlightType.STRIKE:
return StrikeTaskPage(flight, self.dark_kneeboard, self.game.theater)
return StrikeTaskPage(flight, self.dark_kneeboard)
return None
def generate_flight_kneeboard(self, flight: FlightData) -> List[KneeboardPage]:
@@ -767,7 +750,6 @@ class KneeboardGenerator(MissionInfoGenerator):
BriefingPage(
flight,
self.game.coalition_for(flight.friendly).bullseye,
self.game.theater,
self.game.conditions.weather,
zoned_time,
self.dark_kneeboard,

View File

@@ -2,9 +2,10 @@ from __future__ import annotations
from uuid import UUID
from dcs.mapping import LatLng
from pydantic import BaseModel
from game.server.leaflet import LeafletLatLon, LeafletPoly, ShapelyUtil
from game.server.leaflet import LeafletPoly, ShapelyUtil
from game.sim.combat import FrozenCombat
from game.sim.combat.aircombat import AirCombat
from game.sim.combat.atip import AtIp
@@ -14,8 +15,8 @@ from game.theater import ConflictTheater
class FrozenCombatJs(BaseModel):
id: UUID
flight_position: LeafletLatLon | None
target_positions: list[LeafletLatLon] | None
flight_position: LatLng | None
target_positions: list[LatLng] | None
footprint: list[LeafletPoly] | None
@staticmethod
@@ -30,20 +31,15 @@ class FrozenCombatJs(BaseModel):
if isinstance(combat, AtIp):
return FrozenCombatJs(
id=combat.id,
flight_position=theater.point_to_ll(combat.flight.position()).as_list(),
target_positions=[
theater.point_to_ll(combat.flight.package.target.position).as_list()
],
flight_position=combat.flight.position().latlng(),
target_positions=[combat.flight.package.target.position.latlng()],
footprint=None,
)
if isinstance(combat, DefendingSam):
return FrozenCombatJs(
id=combat.id,
flight_position=theater.point_to_ll(combat.flight.position()).as_list(),
target_positions=[
theater.point_to_ll(sam.position).as_list()
for sam in combat.air_defenses
],
flight_position=combat.flight.position().latlng(),
target_positions=[sam.position.latlng() for sam in combat.air_defenses],
footprint=None,
)
raise NotImplementedError(f"Unhandled FrozenCombat type: {combat.__class__}")

View File

@@ -22,8 +22,7 @@ class GameUpdateEventsJs(BaseModel):
def from_events(cls, events: GameUpdateEvents, game: Game) -> GameUpdateEventsJs:
return GameUpdateEventsJs(
updated_flights={
f[0].id: game.theater.point_to_ll(f[1]).as_list()
for f in events.updated_flights
f[0].id: f[1].latlng().as_list() for f in events.updated_flights
},
new_combats=[
FrozenCombatJs.for_combat(c, game.theater) for c in events.new_combats

View File

@@ -1,16 +1,16 @@
from __future__ import annotations
from dcs.mapping import LatLng
from pydantic.dataclasses import dataclass
from game.ato import FlightWaypoint
from game.ato.flightwaypointtype import FlightWaypointType
from game.theater import ConflictTheater, LatLon
@dataclass
class FlightWaypointJs:
name: str
position: LatLon
position: LatLng
altitude_ft: float
altitude_reference: str
is_movable: bool
@@ -18,9 +18,7 @@ class FlightWaypointJs:
include_in_path: bool
@staticmethod
def for_waypoint(
waypoint: FlightWaypoint, theater: ConflictTheater
) -> FlightWaypointJs:
def for_waypoint(waypoint: FlightWaypoint) -> FlightWaypointJs:
# Target *points* are the exact location of a unit, whereas the target area is
# only the center of the objective. Allow moving the latter since its exact
# location isn't very important.
@@ -64,7 +62,7 @@ class FlightWaypointJs:
return FlightWaypointJs(
name=waypoint.name,
position=theater.point_to_ll(waypoint.position),
position=waypoint.position.latlng(),
altitude_ft=waypoint.alt.feet,
altitude_reference=waypoint.alt_type,
is_movable=is_movable,

View File

@@ -1,7 +1,7 @@
from datetime import timedelta
from uuid import UUID
from dcs.mapping import LatLng
from dcs.mapping import LatLng, Point
from fastapi import APIRouter, Depends, HTTPException, status
from game import Game
@@ -26,12 +26,10 @@ def all_waypoints_for_flight(
flight.departure.position,
meters(0),
"RADIO",
),
game.theater,
)
)
return [departure] + [
FlightWaypointJs.for_waypoint(w, game.theater)
for w in flight.flight_plan.waypoints
FlightWaypointJs.for_waypoint(w) for w in flight.flight_plan.waypoints
]
@@ -47,7 +45,7 @@ def set_position(
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN)
waypoint = flight.flight_plan.waypoints[waypoint_idx - 1]
waypoint.position = game.theater.ll_to_point(position)
waypoint.position = Point.from_latlng(position, game.theater.terrain)
package_model = (
GameContext.get_model()
.ato_model_for(flight.blue)

View File

@@ -1,15 +1,10 @@
from __future__ import annotations
from dataclasses import dataclass
from typing import Dict, TYPE_CHECKING
from typing import Dict
from dcs import Point
from .latlon import LatLon
if TYPE_CHECKING:
from .conflicttheater import ConflictTheater
@dataclass
class Bullseye:
@@ -17,6 +12,3 @@ class Bullseye:
def to_pydcs(self) -> Dict[str, float]:
return {"x": self.position.x, "y": self.position.y}
def to_lat_lon(self, theater: ConflictTheater) -> LatLon:
return theater.point_to_ll(self.position)

View File

@@ -1,10 +0,0 @@
# DO NOT EDIT:
# This file is generated by resources/tools/export_coordinates.py.
from game.theater.projections import TransverseMercator
PARAMETERS = TransverseMercator(
central_meridian=33,
false_easting=-99516.9999999732,
false_northing=-4998114.999999984,
scale_factor=0.9996,
)

View File

@@ -4,9 +4,9 @@ import datetime
import math
from dataclasses import dataclass
from pathlib import Path
from typing import Any, Dict, Iterator, List, Optional, TYPE_CHECKING, Tuple
from typing import Dict, Iterator, List, Optional, TYPE_CHECKING, Tuple
from dcs.mapping import LatLng, Point
from dcs.mapping import Point
from dcs.terrain import (
caucasus,
marianaislands,
@@ -17,13 +17,10 @@ from dcs.terrain import (
thechannel,
)
from dcs.terrain.terrain import Terrain
from pyproj import CRS, Transformer
from shapely import geometry, ops
from .frontline import FrontLine
from .landmap import Landmap, load_landmap, poly_contains
from .latlon import LatLon
from .projections import TransverseMercator
from .seasonalconditions import SeasonalConditions
from ..utils import Heading
@@ -50,36 +47,12 @@ class ConflictTheater:
def __init__(self) -> None:
self.controlpoints: List[ControlPoint] = []
self.point_to_ll_transformer = Transformer.from_crs(
self.projection_parameters.to_crs(), CRS("WGS84")
)
self.ll_to_point_transformer = Transformer.from_crs(
CRS("WGS84"), self.projection_parameters.to_crs()
)
"""
self.land_poly = geometry.Polygon(self.landmap[0][0])
for x in self.landmap[1]:
self.land_poly = self.land_poly.difference(geometry.Polygon(x))
"""
def __getstate__(self) -> Dict[str, Any]:
state = self.__dict__.copy()
# Avoid persisting any volatile types that can be deterministically
# recomputed on load for the sake of save compatibility.
del state["point_to_ll_transformer"]
del state["ll_to_point_transformer"]
return state
def __setstate__(self, state: Dict[str, Any]) -> None:
self.__dict__.update(state)
# Regenerate any state that was not persisted.
self.point_to_ll_transformer = Transformer.from_crs(
self.projection_parameters.to_crs(), CRS("WGS84")
)
self.ll_to_point_transformer = Transformer.from_crs(
CRS("WGS84"), self.projection_parameters.to_crs()
)
def add_controlpoint(self, point: ControlPoint) -> None:
self.controlpoints.append(point)
@@ -253,17 +226,6 @@ class ConflictTheater:
def seasonal_conditions(self) -> SeasonalConditions:
raise NotImplementedError
@property
def projection_parameters(self) -> TransverseMercator:
raise NotImplementedError
def point_to_ll(self, point: Point) -> LatLon:
lat, lon = self.point_to_ll_transformer.transform(point.x, point.y)
return LatLon(lat, lon)
def ll_to_point(self, ll: LatLng) -> Point:
return Point.from_latlng(ll, self.terrain)
def heading_to_conflict_from(self, position: Point) -> Optional[Heading]:
# Heading for a Group to the enemy.
# Should be the point between the nearest and the most distant conflict
@@ -309,12 +271,6 @@ class CaucasusTheater(ConflictTheater):
return CONDITIONS
@property
def projection_parameters(self) -> TransverseMercator:
from .caucasus import PARAMETERS
return PARAMETERS
class PersianGulfTheater(ConflictTheater):
terrain = persiangulf.PersianGulf()
@@ -337,12 +293,6 @@ class PersianGulfTheater(ConflictTheater):
return CONDITIONS
@property
def projection_parameters(self) -> TransverseMercator:
from .persiangulf import PARAMETERS
return PARAMETERS
class NevadaTheater(ConflictTheater):
terrain = nevada.Nevada()
@@ -365,12 +315,6 @@ class NevadaTheater(ConflictTheater):
return CONDITIONS
@property
def projection_parameters(self) -> TransverseMercator:
from .nevada import PARAMETERS
return PARAMETERS
class NormandyTheater(ConflictTheater):
terrain = normandy.Normandy()
@@ -393,12 +337,6 @@ class NormandyTheater(ConflictTheater):
return CONDITIONS
@property
def projection_parameters(self) -> TransverseMercator:
from .normandy import PARAMETERS
return PARAMETERS
class TheChannelTheater(ConflictTheater):
terrain = thechannel.TheChannel()
@@ -421,12 +359,6 @@ class TheChannelTheater(ConflictTheater):
return CONDITIONS
@property
def projection_parameters(self) -> TransverseMercator:
from .thechannel import PARAMETERS
return PARAMETERS
class SyriaTheater(ConflictTheater):
terrain = syria.Syria()
@@ -449,12 +381,6 @@ class SyriaTheater(ConflictTheater):
return CONDITIONS
@property
def projection_parameters(self) -> TransverseMercator:
from .syria import PARAMETERS
return PARAMETERS
class MarianaIslandsTheater(ConflictTheater):
terrain = marianaislands.MarianaIslands()
@@ -477,9 +403,3 @@ class MarianaIslandsTheater(ConflictTheater):
from .seasonalconditions.marianaislands import CONDITIONS
return CONDITIONS
@property
def projection_parameters(self) -> TransverseMercator:
from .marianaislands import PARAMETERS
return PARAMETERS

View File

@@ -1,36 +0,0 @@
from typing import List, Tuple
from pydantic.dataclasses import dataclass
@dataclass(frozen=True)
class LatLon:
# These names match Leaflet for easier interop.
lat: float
lng: float
def as_list(self) -> List[float]:
return [self.lat, self.lng]
@staticmethod
def _components(dimension: float) -> Tuple[int, int, float]:
degrees = int(dimension)
minutes = int(dimension * 60 % 60)
seconds = dimension * 3600 % 60
return degrees, minutes, seconds
def _format_component(
self, dimension: float, hemispheres: Tuple[str, str], seconds_precision: int
) -> str:
hemisphere = hemispheres[0] if dimension >= 0 else hemispheres[1]
degrees, minutes, seconds = self._components(dimension)
return f"{degrees}°{minutes:02}'{seconds:02.{seconds_precision}f}\"{hemisphere}"
def format_dms(self, include_decimal_seconds: bool = False) -> str:
precision = 2 if include_decimal_seconds else 0
return " ".join(
[
self._format_component(self.lat, ("N", "S"), precision),
self._format_component(self.lng, ("E", "W"), precision),
]
)

View File

@@ -1,10 +0,0 @@
# DO NOT EDIT:
# This file is generated by resources/tools/export_coordinates.py.
from game.theater.projections import TransverseMercator
PARAMETERS = TransverseMercator(
central_meridian=147,
false_easting=238417.99999989968,
false_northing=-1491840.000000048,
scale_factor=0.9996,
)

View File

@@ -1,10 +0,0 @@
# DO NOT EDIT:
# This file is generated by resources/tools/export_coordinates.py.
from game.theater.projections import TransverseMercator
PARAMETERS = TransverseMercator(
central_meridian=-117,
false_easting=-193996.80999964548,
false_northing=-4410028.063999966,
scale_factor=0.9996,
)

View File

@@ -1,10 +0,0 @@
# DO NOT EDIT:
# This file is generated by resources/tools/export_coordinates.py.
from game.theater.projections import TransverseMercator
PARAMETERS = TransverseMercator(
central_meridian=-3,
false_easting=-195526.00000000204,
false_northing=-5484812.999999951,
scale_factor=0.9996,
)

View File

@@ -1,10 +0,0 @@
# DO NOT EDIT:
# This file is generated by resources/tools/export_coordinates.py.
from game.theater.projections import TransverseMercator
PARAMETERS = TransverseMercator(
central_meridian=57,
false_easting=75755.99999999645,
false_northing=-2894933.0000000377,
scale_factor=0.9996,
)

View File

@@ -1,31 +0,0 @@
from dataclasses import dataclass
from pyproj import CRS
@dataclass(frozen=True)
class TransverseMercator:
central_meridian: int
false_easting: float
false_northing: float
scale_factor: float
def to_crs(self) -> CRS:
return CRS.from_proj4(
" ".join(
[
"+proj=tmerc",
"+lat_0=0",
f"+lon_0={self.central_meridian}",
f"+k_0={self.scale_factor}",
f"+x_0={self.false_easting}",
f"+y_0={self.false_northing}",
"+towgs84=0,0,0,0,0,0,0",
"+units=m",
"+vunits=m",
"+ellps=WGS84",
"+no_defs",
"+axis=neu",
]
)
)

View File

@@ -1,10 +0,0 @@
# DO NOT EDIT:
# This file is generated by resources/tools/export_coordinates.py.
from game.theater.projections import TransverseMercator
PARAMETERS = TransverseMercator(
central_meridian=39,
false_easting=282801.00000003993,
false_northing=-3879865.9999999935,
scale_factor=0.9996,
)

View File

@@ -1,10 +0,0 @@
# DO NOT EDIT:
# This file is generated by resources/tools/export_coordinates.py.
from game.theater.projections import TransverseMercator
PARAMETERS = TransverseMercator(
central_meridian=3,
false_easting=99376.00000000288,
false_northing=-5636889.00000001,
scale_factor=0.9996,
)