diff --git a/deebot_client/map.py b/deebot_client/map.py index dc938b7e..40432659 100644 --- a/deebot_client/map.py +++ b/deebot_client/map.py @@ -17,7 +17,6 @@ from PIL import Image, ImageColor, ImageOps, ImagePalette import svg -from deebot_client import util from deebot_client.events.map import CachedMapInfoEvent, MapChangedEvent from .commands.json import GetMinorMap @@ -39,6 +38,7 @@ from .util import ( OnChangedDict, OnChangedList, + decompress_7z_base64_data, ) if TYPE_CHECKING: @@ -163,6 +163,21 @@ class BackgroundImage: image: bytes +class ViewBoxFloat: + """ViewBox where all values are converted to float.""" + + def __init__(self, view_box: svg.ViewBoxSpec) -> None: + self.min_x = float(view_box.min_x) + self.min_y = float(view_box.min_y) + self.width = float(view_box.width) + self.height = float(view_box.height) + self.max_x = self.min_x + self.width + self.max_y = self.min_y + self.height + + def __str__(self) -> str: + return f"{self.min_x} {self.min_y} {self.width} {self.height}" + + # SVG definitions referred by map elements _SVG_DEFS = svg.Defs( elements=[ @@ -221,16 +236,16 @@ def _calc_point( ) -def _calc_point_in_viewbox(x: float, y: float, viewbox: svg.ViewBoxSpec) -> Point: +def _calc_point_in_viewbox(x: float, y: float, view_box: ViewBoxFloat) -> Point: point = _calc_point(x, y) return Point( min( - max(point.x, float(viewbox.min_x)), - float(viewbox.min_x) + float(viewbox.width), + max(point.x, view_box.min_x), + view_box.max_x, ), min( - max(point.y, float(viewbox.min_y)), - float(viewbox.min_y) + float(viewbox.height), + max(point.y, view_box.min_y), + view_box.max_y, ), ) @@ -263,11 +278,11 @@ def _points_to_svg_path( def _get_svg_positions( - positions: list[Position], viewbox: svg.ViewBoxSpec + positions: list[Position], view_box: ViewBoxFloat ) -> list[svg.Element]: svg_positions: list[svg.Element] = [] for position in sorted(positions, key=lambda x: _POSITIONS_SVG[x.type].order): - pos = _calc_point_in_viewbox(position.x, position.y, viewbox) + pos = _calc_point_in_viewbox(position.x, position.y, view_box) svg_positions.append( svg.Use(href=f"#{_POSITIONS_SVG[position.type].svg_id}", x=pos.x, y=pos.y) ) @@ -559,7 +574,7 @@ def get_svg_map(self) -> str | None: # Bot and Charge stations svg_map.elements.extend( - _get_svg_positions(self._map_data.positions, svg_map.viewBox) + _get_svg_positions(self._map_data.positions, ViewBoxFloat(svg_map.viewBox)) ) self._last_image = str(svg_map) diff --git a/tests/test_map.py b/tests/test_map.py index 16126079..8376aba2 100644 --- a/tests/test_map.py +++ b/tests/test_map.py @@ -40,6 +40,7 @@ Path, Point, TracePoint, + ViewBoxFloat, _calc_point, _calc_point_in_viewbox, _get_svg_positions, @@ -81,15 +82,15 @@ def test_calc_point( @pytest.mark.parametrize( - ("x", "y", "viewbox", "expected"), _test_calc_point_in_viewbox_data + ("x", "y", "view_box", "expected"), _test_calc_point_in_viewbox_data ) def test_calc_point_in_viewbox( x: int, y: int, - viewbox: ViewBoxSpec, + view_box: ViewBoxSpec, expected: Point, ) -> None: - result = _calc_point_in_viewbox(x, y, viewbox) + result = _calc_point_in_viewbox(x, y, ViewBoxFloat(view_box)) assert result == expected @@ -157,7 +158,7 @@ async def on_change() -> None: @patch( - "deebot_client.util.decompress_7z_base64_data", + "deebot_client.map.decompress_7z_base64_data", Mock(return_value=b"\x10\x00\x00\x01\x00"), ) async def test_Map_svg_traces_path( @@ -309,12 +310,12 @@ def test_get_svg_subset(subset: MapSubsetEvent, expected: Path | Polygon) -> Non @pytest.mark.parametrize( - ("positions", "viewbox", "expected"), _test_get_svg_positions_data + ("positions", "view_box", "expected"), _test_get_svg_positions_data ) def test_get_svg_positions( positions: list[Position], - viewbox: ViewBoxSpec, + view_box: ViewBoxSpec, expected: list[Use], ) -> None: - result = _get_svg_positions(positions, viewbox) + result = _get_svg_positions(positions, ViewBoxFloat(view_box)) assert result == expected