mirror of
https://github.com/dcs-liberation/dcs_liberation.git
synced 2025-11-10 14:22:26 +00:00
Add ability to convert landmap to/from miz.
This PR adds utility functions that import/export landmap files to .miz polygons. In addition to the unit test, this PR has been tested by writing the Caucuses & Syria landmaps to a .miz file, loading the generated .miz file back in and checking that the loaded landmap object is identical to the original files.
This commit is contained in:
parent
29ffb526f2
commit
e50ee976ed
@ -4,10 +4,17 @@ from functools import cached_property
|
||||
from typing import Optional, Tuple, Union
|
||||
import logging
|
||||
from pathlib import Path
|
||||
from typing import List
|
||||
|
||||
from shapely import geometry
|
||||
from shapely.geometry import MultiPolygon, Polygon
|
||||
|
||||
from dcs.drawing.drawing import LineStyle, Rgba
|
||||
from dcs.drawing.polygon import FreeFormPolygon
|
||||
from dcs.mapping import Point
|
||||
from dcs.mission import Mission
|
||||
from dcs.terrain.terrain import Terrain
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class Landmap:
|
||||
@ -39,3 +46,94 @@ def load_landmap(filename: Path) -> Optional[Landmap]:
|
||||
|
||||
def poly_contains(x: float, y: float, poly: Union[MultiPolygon, Polygon]) -> bool:
|
||||
return poly.contains(geometry.Point(x, y))
|
||||
|
||||
|
||||
def to_miz(landmap: Landmap, terrain: Terrain, mission_filename: str) -> None:
|
||||
"""
|
||||
Writes landmap to .miz file so that zones can be visualized and edited in the
|
||||
mission editor.
|
||||
"""
|
||||
|
||||
def multi_polygon_to_miz(
|
||||
mission: Mission,
|
||||
terrain: Terrain,
|
||||
multi_polygon: MultiPolygon,
|
||||
color: Rgba,
|
||||
prefix: str,
|
||||
layer_index: int = 4,
|
||||
layer_name: str = "Author",
|
||||
) -> None:
|
||||
reference_position = Point(0, 0, terrain)
|
||||
for i in range(len(multi_polygon.geoms)):
|
||||
polygon = multi_polygon.geoms[i]
|
||||
if len(polygon.interiors) > 0:
|
||||
raise ValueError(
|
||||
f"Polygon hole found when trying to export {prefix} {i}. to_miz() does not support landmap zones with holes."
|
||||
)
|
||||
coordinates = polygon.exterior.xy
|
||||
points = []
|
||||
for j in range(len(coordinates[0])):
|
||||
points.append(Point(coordinates[0][j], coordinates[1][j], terrain))
|
||||
polygon_drawing = FreeFormPolygon(
|
||||
visible=True,
|
||||
position=reference_position,
|
||||
name=f"{prefix}-{i}",
|
||||
color=color,
|
||||
layer_name=layer_name,
|
||||
fill=color,
|
||||
line_thickness=10,
|
||||
line_style=LineStyle.Solid,
|
||||
points=points,
|
||||
)
|
||||
mission.drawings.layers[layer_index].objects.append(polygon_drawing)
|
||||
|
||||
mission = Mission(terrain=terrain)
|
||||
multi_polygon_to_miz(
|
||||
mission, terrain, landmap.exclusion_zones, Rgba(255, 0, 0, 128), "Exclusion"
|
||||
)
|
||||
multi_polygon_to_miz(
|
||||
mission, terrain, landmap.sea_zones, Rgba(0, 0, 255, 128), "Sea"
|
||||
)
|
||||
multi_polygon_to_miz(
|
||||
mission, terrain, landmap.inclusion_zones, Rgba(0, 255, 0, 128), "Inclusion"
|
||||
)
|
||||
mission.save(mission_filename)
|
||||
|
||||
|
||||
def from_miz(mission_filename: str, layer_index: int = 4) -> Landmap:
|
||||
"""
|
||||
Generate Landmap object from Free Form Polygons drawn in a .miz file.
|
||||
Landmap.inclusion_zones are expected to be named Inclusion-<suffix>
|
||||
Landmap.exclusion_zones are expected to be named Exclusion-<suffix>
|
||||
Landmap.sea_zones are expected to be named Sea-<suffix>
|
||||
"""
|
||||
mission = Mission()
|
||||
mission.load_file(mission_filename)
|
||||
polygons: dict[str, List[Polygon]] = {"Inclusion": [], "Exclusion": [], "Sea": []}
|
||||
for draw_object in mission.drawings.layers[layer_index].objects:
|
||||
if type(draw_object) != FreeFormPolygon:
|
||||
logging.debug(
|
||||
f"Object {draw_object.name} is not a FreeFormPolygon, ignoring"
|
||||
)
|
||||
continue
|
||||
name_split = draw_object.name.split(
|
||||
"-"
|
||||
) # names are in the format <Inclusion|Exclusion|Sea>-<suffix>
|
||||
zone_type = name_split[0]
|
||||
if len(name_split) != 2 or zone_type not in ("Exclusion", "Sea", "Inclusion"):
|
||||
logging.debug(
|
||||
f"Object name {draw_object.name} does not conform to expected format <Exclusion|Sea|Inclusion>-<suffix>, ignoring"
|
||||
)
|
||||
continue
|
||||
polygon_points = []
|
||||
for point in draw_object.points:
|
||||
polygon_points.append(
|
||||
(point.x + draw_object.position.x, point.y + draw_object.position.y)
|
||||
)
|
||||
polygons[zone_type].append(Polygon(polygon_points))
|
||||
landmap = Landmap(
|
||||
inclusion_zones=MultiPolygon(polygons["Inclusion"]),
|
||||
exclusion_zones=MultiPolygon(polygons["Exclusion"]),
|
||||
sea_zones=MultiPolygon(polygons["Sea"]),
|
||||
)
|
||||
return landmap
|
||||
|
||||
32
tests/theater/test_landmap.py
Normal file
32
tests/theater/test_landmap.py
Normal file
@ -0,0 +1,32 @@
|
||||
import os
|
||||
import pytest
|
||||
|
||||
from shapely.geometry import MultiPolygon, Polygon
|
||||
|
||||
from dcs.terrain.caucasus.caucasus import Caucasus
|
||||
from game.theater import landmap
|
||||
|
||||
|
||||
def test_miz() -> None:
|
||||
"""
|
||||
Test miz generation and loading
|
||||
"""
|
||||
test_map = landmap.Landmap(
|
||||
inclusion_zones=MultiPolygon([Polygon([(0, 0), (0, 1), (1, 0)])]),
|
||||
exclusion_zones=MultiPolygon([Polygon([(1, 1), (0, 1), (1, 0)])]),
|
||||
sea_zones=MultiPolygon([Polygon([(0, 0), (0, 2), (1, 0)])]),
|
||||
)
|
||||
test_filename = "test.miz"
|
||||
landmap.to_miz(test_map, Caucasus(), test_filename)
|
||||
assert os.path.isfile("test.miz")
|
||||
loaded_map = landmap.from_miz("test.miz")
|
||||
assert test_map.inclusion_zones.equals_exact(
|
||||
loaded_map.inclusion_zones, tolerance=1e-6
|
||||
)
|
||||
assert test_map.sea_zones.equals_exact(loaded_map.sea_zones, tolerance=1e-6)
|
||||
assert test_map.exclusion_zones.equals_exact(
|
||||
loaded_map.exclusion_zones, tolerance=1e-6
|
||||
)
|
||||
|
||||
if os.path.isfile(test_filename):
|
||||
os.remove(test_filename)
|
||||
Loading…
x
Reference in New Issue
Block a user