Dan Albert 2dba0f07ad
Support polygons with holes in the API.
We don't have any of these yet because our landmaps suck, but we'll need
holes in the sea zones to mask islands correctly.
2022-09-17 14:35:00 +02:00

75 lines
2.2 KiB
Python

from __future__ import annotations
from typing import Union
from dcs import Point
from dcs.mapping import LatLng
from pydantic import BaseModel
from shapely.geometry import LineString, MultiLineString, MultiPolygon, Polygon
from game.theater import ConflictTheater
class LeafletPoint(BaseModel):
lat: float
lng: float
class Config:
orm_mode = True
title = "LatLng"
LeafletLine = list[LeafletPoint]
# Leaflet allows either a single array of latlng to define the boundary, or an array of
# arrays of latlngs, with the first array being the boundary and the rest defining
# holes. To avoid the UI needing to test for which type of polygon we send, we always
# use the array of arrays type, even for geometries without holes.
# https://leafletjs.com/reference.html#polygon
LeafletPoly = list[LeafletLine]
class ShapelyUtil:
@staticmethod
def latlng_to_leaflet(latlng: LatLng) -> LeafletPoint:
return LeafletPoint(lat=latlng.lat, lng=latlng.lng)
@classmethod
def poly_to_leaflet(cls, poly: Polygon, theater: ConflictTheater) -> LeafletPoly:
if poly.is_empty:
return []
try:
lines = poly.boundary.geoms
except AttributeError:
lines = [poly.boundary]
return cls.lines_to_leaflet(MultiLineString(lines), theater)
@classmethod
def polys_to_leaflet(
cls, poly: Union[Polygon, MultiPolygon], theater: ConflictTheater
) -> list[LeafletPoly]:
if isinstance(poly, MultiPolygon):
polys = poly.geoms
else:
polys = [poly]
return [cls.poly_to_leaflet(poly, theater) for poly in polys]
@classmethod
def line_to_leaflet(cls, line: LineString, theater: ConflictTheater) -> LeafletLine:
return [
cls.latlng_to_leaflet(Point(x, y, theater.terrain).latlng())
for x, y in line.coords
]
@classmethod
def lines_to_leaflet(
cls, line_string: MultiLineString | LineString, theater: ConflictTheater
) -> list[LeafletLine]:
if isinstance(line_string, MultiLineString):
lines = line_string.geoms
else:
lines = [line_string]
return [cls.line_to_leaflet(line, theater) for line in lines]