valetudo-map-parser 0.1.9b55__py3-none-any.whl → 0.1.9b57__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- valetudo_map_parser/__init__.py +6 -2
- valetudo_map_parser/config/auto_crop.py +150 -20
- valetudo_map_parser/config/shared.py +47 -1
- valetudo_map_parser/config/types.py +2 -1
- valetudo_map_parser/config/utils.py +91 -2
- valetudo_map_parser/hypfer_draw.py +104 -49
- valetudo_map_parser/hypfer_handler.py +69 -19
- valetudo_map_parser/map_data.py +26 -2
- valetudo_map_parser/{rand25_handler.py → rand256_handler.py} +301 -100
- valetudo_map_parser/rooms_handler.py +6 -2
- {valetudo_map_parser-0.1.9b55.dist-info → valetudo_map_parser-0.1.9b57.dist-info}/METADATA +1 -1
- valetudo_map_parser-0.1.9b57.dist-info/RECORD +26 -0
- valetudo_map_parser/config/room_outline.py +0 -148
- valetudo_map_parser-0.1.9b55.dist-info/RECORD +0 -27
- {valetudo_map_parser-0.1.9b55.dist-info → valetudo_map_parser-0.1.9b57.dist-info}/LICENSE +0 -0
- {valetudo_map_parser-0.1.9b55.dist-info → valetudo_map_parser-0.1.9b57.dist-info}/NOTICE.txt +0 -0
- {valetudo_map_parser-0.1.9b55.dist-info → valetudo_map_parser-0.1.9b57.dist-info}/WHEEL +0 -0
@@ -21,11 +21,13 @@ from .config.types import (
|
|
21
21
|
Colors,
|
22
22
|
RoomsProperties,
|
23
23
|
RoomStore,
|
24
|
+
WebPBytes,
|
24
25
|
)
|
25
26
|
from .config.utils import (
|
26
27
|
BaseHandler,
|
27
28
|
initialize_drawing_config,
|
28
29
|
manage_drawable_elements,
|
30
|
+
numpy_to_webp_bytes,
|
29
31
|
prepare_resize_params,
|
30
32
|
)
|
31
33
|
from .hypfer_draw import ImageDraw as ImDraw
|
@@ -81,7 +83,11 @@ class HypferMapImageHandler(BaseHandler, AutoCrop):
|
|
81
83
|
self.rooms_pos = []
|
82
84
|
for room_id, room_data in room_properties.items():
|
83
85
|
self.rooms_pos.append(
|
84
|
-
{
|
86
|
+
{
|
87
|
+
"id": room_id,
|
88
|
+
"name": room_data["name"],
|
89
|
+
"outline": room_data["outline"],
|
90
|
+
}
|
85
91
|
)
|
86
92
|
else:
|
87
93
|
LOGGER.debug("%s: Rooms data not available!", self.file_name)
|
@@ -92,12 +98,14 @@ class HypferMapImageHandler(BaseHandler, AutoCrop):
|
|
92
98
|
async def async_get_image_from_json(
|
93
99
|
self,
|
94
100
|
m_json: json | None,
|
95
|
-
|
101
|
+
return_webp: bool = False,
|
102
|
+
) -> WebPBytes | Image.Image | None:
|
96
103
|
"""Get the image from the JSON data.
|
97
104
|
It uses the ImageDraw class to draw some of the elements of the image.
|
98
105
|
The robot itself will be drawn in this function as per some of the values are needed for other tasks.
|
99
106
|
@param m_json: The JSON data to use to draw the image.
|
100
|
-
@
|
107
|
+
@param return_webp: If True, return WebP bytes; if False, return PIL Image (default).
|
108
|
+
@return WebPBytes | Image.Image: WebP bytes or PIL Image depending on return_webp parameter.
|
101
109
|
"""
|
102
110
|
# Initialize the colors.
|
103
111
|
colors: Colors = {
|
@@ -131,6 +139,8 @@ class HypferMapImageHandler(BaseHandler, AutoCrop):
|
|
131
139
|
# Get the pixels size and layers from the JSON data
|
132
140
|
pixel_size = int(m_json["pixelSize"])
|
133
141
|
layers, active = self.data.find_layers(m_json["layers"], {}, [])
|
142
|
+
# Populate active_zones from the JSON data
|
143
|
+
self.active_zones = active
|
134
144
|
new_frame_hash = await self.calculate_array_hash(layers, active)
|
135
145
|
if self.frame_number == 0:
|
136
146
|
self.img_hash = new_frame_hash
|
@@ -240,20 +250,30 @@ class HypferMapImageHandler(BaseHandler, AutoCrop):
|
|
240
250
|
|
241
251
|
# Draw obstacles if enabled
|
242
252
|
if self.drawing_config.is_enabled(DrawableElement.OBSTACLE):
|
243
|
-
|
244
|
-
|
245
|
-
|
253
|
+
self.shared.obstacles_pos = self.data.get_obstacles(entity_dict)
|
254
|
+
if self.shared.obstacles_pos:
|
255
|
+
img_np_array = await self.imd.async_draw_obstacle(
|
256
|
+
img_np_array, self.shared.obstacles_pos, colors["no_go"]
|
257
|
+
)
|
246
258
|
# Robot and rooms position
|
247
259
|
if (room_id > 0) and not self.room_propriety:
|
248
260
|
self.room_propriety = await self.async_extract_room_properties(
|
249
261
|
self.json_data
|
250
262
|
)
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
263
|
+
|
264
|
+
# Ensure room data is available for robot room detection (even if not extracted above)
|
265
|
+
if not self.rooms_pos and not self.room_propriety:
|
266
|
+
self.room_propriety = await self.async_extract_room_properties(
|
267
|
+
self.json_data
|
268
|
+
)
|
269
|
+
|
270
|
+
# Always check robot position for zooming (moved outside the condition)
|
271
|
+
if self.rooms_pos and robot_position and robot_position_angle:
|
272
|
+
self.robot_pos = await self.imd.async_get_robot_in_room(
|
273
|
+
robot_x=(robot_position[0]),
|
274
|
+
robot_y=(robot_position[1]),
|
275
|
+
angle=robot_position_angle,
|
276
|
+
)
|
257
277
|
LOGGER.info("%s: Completed base Layers", self.file_name)
|
258
278
|
# Copy the new array in base layer.
|
259
279
|
self.img_base_layer = await self.async_copy_array(img_np_array)
|
@@ -332,6 +352,9 @@ class HypferMapImageHandler(BaseHandler, AutoCrop):
|
|
332
352
|
robot_position,
|
333
353
|
DrawableElement.ROBOT,
|
334
354
|
)
|
355
|
+
# Synchronize zooming state from ImageDraw to handler before auto-crop
|
356
|
+
self.zooming = self.imd.img_h.zooming
|
357
|
+
|
335
358
|
# Resize the image
|
336
359
|
img_np_array = await self.async_auto_trim_and_zoom_image(
|
337
360
|
img_np_array,
|
@@ -345,16 +368,43 @@ class HypferMapImageHandler(BaseHandler, AutoCrop):
|
|
345
368
|
LOGGER.warning("%s: Image array is None.", self.file_name)
|
346
369
|
return None
|
347
370
|
|
348
|
-
#
|
349
|
-
pil_img = Image.fromarray(img_np_array, mode="RGBA")
|
350
|
-
del img_np_array
|
351
|
-
# reduce the image size if the zoomed image is bigger then the original.
|
371
|
+
# Handle resizing if needed, then return based on format preference
|
352
372
|
if self.check_zoom_and_aspect_ratio():
|
373
|
+
# Convert to PIL for resizing
|
374
|
+
pil_img = Image.fromarray(img_np_array, mode="RGBA")
|
375
|
+
del img_np_array
|
353
376
|
resize_params = prepare_resize_params(self, pil_img, False)
|
354
377
|
resized_image = await self.async_resize_images(resize_params)
|
355
|
-
|
356
|
-
|
357
|
-
|
378
|
+
|
379
|
+
# Return WebP bytes or PIL Image based on parameter
|
380
|
+
if return_webp:
|
381
|
+
from .config.utils import pil_to_webp_bytes
|
382
|
+
webp_bytes = await pil_to_webp_bytes(
|
383
|
+
resized_image,
|
384
|
+
quality=90,
|
385
|
+
lossless=False
|
386
|
+
)
|
387
|
+
return webp_bytes
|
388
|
+
else:
|
389
|
+
return resized_image
|
390
|
+
else:
|
391
|
+
# Return WebP bytes or PIL Image based on parameter
|
392
|
+
if return_webp:
|
393
|
+
# Convert directly from NumPy to WebP for better performance
|
394
|
+
webp_bytes = await numpy_to_webp_bytes(
|
395
|
+
img_np_array,
|
396
|
+
quality=90,
|
397
|
+
lossless=False
|
398
|
+
)
|
399
|
+
del img_np_array
|
400
|
+
LOGGER.debug("%s: Frame Completed.", self.file_name)
|
401
|
+
return webp_bytes
|
402
|
+
else:
|
403
|
+
# Convert to PIL Image (original behavior)
|
404
|
+
pil_img = Image.fromarray(img_np_array, mode="RGBA")
|
405
|
+
del img_np_array
|
406
|
+
LOGGER.debug("%s: Frame Completed.", self.file_name)
|
407
|
+
return pil_img
|
358
408
|
except (RuntimeError, RuntimeWarning) as e:
|
359
409
|
LOGGER.warning(
|
360
410
|
"%s: Error %s during image creation.",
|
valetudo_map_parser/map_data.py
CHANGED
@@ -10,7 +10,7 @@ from __future__ import annotations
|
|
10
10
|
|
11
11
|
import numpy as np
|
12
12
|
|
13
|
-
from .config.types import
|
13
|
+
from .config.types import ImageSize, JsonType
|
14
14
|
|
15
15
|
|
16
16
|
class ImageData:
|
@@ -34,6 +34,30 @@ class ImageData:
|
|
34
34
|
# list the specific Layers, Paths, Zones and Pints in the
|
35
35
|
# Vacuums Json in parallel.
|
36
36
|
|
37
|
+
@staticmethod
|
38
|
+
def get_obstacles(entity_dict: dict) -> list:
|
39
|
+
"""Get the obstacles positions from the entity data."""
|
40
|
+
try:
|
41
|
+
obstacle_data = entity_dict.get("obstacle")
|
42
|
+
except KeyError:
|
43
|
+
return []
|
44
|
+
obstacle_positions = []
|
45
|
+
if obstacle_data:
|
46
|
+
for obstacle in obstacle_data:
|
47
|
+
label = obstacle.get("metaData", {}).get("label")
|
48
|
+
points = obstacle.get("points", [])
|
49
|
+
image_id = obstacle.get("metaData", {}).get("id")
|
50
|
+
|
51
|
+
if label and points:
|
52
|
+
obstacle_pos = {
|
53
|
+
"label": label,
|
54
|
+
"points": {"x": points[0], "y": points[1]},
|
55
|
+
"id": image_id,
|
56
|
+
}
|
57
|
+
obstacle_positions.append(obstacle_pos)
|
58
|
+
return obstacle_positions
|
59
|
+
return []
|
60
|
+
|
37
61
|
@staticmethod
|
38
62
|
def find_layers(
|
39
63
|
json_obj: JsonType, layer_dict: dict, active_list: list
|
@@ -284,7 +308,7 @@ class RandImageData:
|
|
284
308
|
Return the calculated angle and original angle.
|
285
309
|
"""
|
286
310
|
angle_c = round(json_data.get("robot_angle", 0))
|
287
|
-
angle = (360 - angle_c +
|
311
|
+
angle = (360 - angle_c + 95) if angle_c < 0 else (180 - angle_c - 85)
|
288
312
|
return angle % 360, json_data.get("robot_angle", 0)
|
289
313
|
|
290
314
|
@staticmethod
|