valetudo-map-parser 0.1.7__py3-none-any.whl → 0.1.9__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 +28 -13
- valetudo_map_parser/config/async_utils.py +93 -0
- valetudo_map_parser/config/auto_crop.py +312 -123
- valetudo_map_parser/config/color_utils.py +105 -0
- valetudo_map_parser/config/colors.py +662 -13
- valetudo_map_parser/config/drawable.py +613 -268
- valetudo_map_parser/config/drawable_elements.py +292 -0
- valetudo_map_parser/config/enhanced_drawable.py +324 -0
- valetudo_map_parser/config/optimized_element_map.py +406 -0
- valetudo_map_parser/config/rand256_parser.py +395 -0
- valetudo_map_parser/config/shared.py +94 -11
- valetudo_map_parser/config/types.py +105 -52
- valetudo_map_parser/config/utils.py +1025 -0
- valetudo_map_parser/hypfer_draw.py +464 -148
- valetudo_map_parser/hypfer_handler.py +366 -259
- valetudo_map_parser/hypfer_rooms_handler.py +599 -0
- valetudo_map_parser/map_data.py +56 -66
- valetudo_map_parser/rand256_handler.py +674 -0
- valetudo_map_parser/reimg_draw.py +68 -84
- valetudo_map_parser/rooms_handler.py +474 -0
- valetudo_map_parser-0.1.9.dist-info/METADATA +93 -0
- valetudo_map_parser-0.1.9.dist-info/RECORD +27 -0
- {valetudo_map_parser-0.1.7.dist-info → valetudo_map_parser-0.1.9.dist-info}/WHEEL +1 -1
- valetudo_map_parser/config/rand25_parser.py +0 -398
- valetudo_map_parser/images_utils.py +0 -398
- valetudo_map_parser/rand25_handler.py +0 -455
- valetudo_map_parser-0.1.7.dist-info/METADATA +0 -23
- valetudo_map_parser-0.1.7.dist-info/RECORD +0 -20
- {valetudo_map_parser-0.1.7.dist-info → valetudo_map_parser-0.1.9.dist-info}/LICENSE +0 -0
- {valetudo_map_parser-0.1.7.dist-info → valetudo_map_parser-0.1.9.dist-info}/NOTICE.txt +0 -0
valetudo_map_parser/map_data.py
CHANGED
|
@@ -10,47 +10,12 @@ from __future__ import annotations
|
|
|
10
10
|
|
|
11
11
|
import numpy as np
|
|
12
12
|
|
|
13
|
-
from .config.types import
|
|
14
|
-
Colors,
|
|
15
|
-
ImageSize,
|
|
16
|
-
JsonType,
|
|
17
|
-
NumpyArray,
|
|
18
|
-
)
|
|
13
|
+
from .config.types import ImageSize, JsonType
|
|
19
14
|
|
|
20
15
|
|
|
21
16
|
class ImageData:
|
|
22
17
|
"""Class to handle the image data."""
|
|
23
18
|
|
|
24
|
-
@staticmethod
|
|
25
|
-
async def async_extract_color_coordinates(
|
|
26
|
-
source_array: NumpyArray, search_for_colours_list: Colors
|
|
27
|
-
) -> list:
|
|
28
|
-
"""Search for specific colors in an image array and return their coordinates."""
|
|
29
|
-
# Initialize an empty list to store color and coordinates tuples
|
|
30
|
-
color_coordinates_list = []
|
|
31
|
-
|
|
32
|
-
# Iterate over the search_for_colours_list
|
|
33
|
-
for color_to_search in search_for_colours_list:
|
|
34
|
-
# Initialize an empty list to store coordinates for the current color
|
|
35
|
-
color_coordinates = []
|
|
36
|
-
|
|
37
|
-
# Iterate over the image array
|
|
38
|
-
for y in range(source_array.shape[0]):
|
|
39
|
-
for x in range(source_array.shape[1]):
|
|
40
|
-
# Extract the pixel color at the current coordinates
|
|
41
|
-
pixel_color = source_array[y, x]
|
|
42
|
-
|
|
43
|
-
# Check if the current pixel color matches the color_to_search
|
|
44
|
-
if np.all(pixel_color == color_to_search):
|
|
45
|
-
# Record the coordinates for the current color
|
|
46
|
-
color_coordinates.append((x, y))
|
|
47
|
-
|
|
48
|
-
# Append the color and its coordinates to the final list
|
|
49
|
-
color_coordinates_list.append((color_to_search, color_coordinates))
|
|
50
|
-
|
|
51
|
-
# Return the final list of color and coordinates tuples
|
|
52
|
-
return color_coordinates_list
|
|
53
|
-
|
|
54
19
|
@staticmethod
|
|
55
20
|
def sublist(lst, n):
|
|
56
21
|
"""Sub lists of specific n number of elements"""
|
|
@@ -69,6 +34,30 @@ class ImageData:
|
|
|
69
34
|
# list the specific Layers, Paths, Zones and Pints in the
|
|
70
35
|
# Vacuums Json in parallel.
|
|
71
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
|
+
|
|
72
61
|
@staticmethod
|
|
73
62
|
def find_layers(
|
|
74
63
|
json_obj: JsonType, layer_dict: dict, active_list: list
|
|
@@ -209,10 +198,11 @@ class ImageData:
|
|
|
209
198
|
max_y * pixel_size,
|
|
210
199
|
)
|
|
211
200
|
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
201
|
+
|
|
202
|
+
class RandImageData:
|
|
203
|
+
"""This functions read directly the data from the json created
|
|
204
|
+
from the parser for Valetudo Re. They allow to use the
|
|
205
|
+
functions to draw the image without changes on the drawing class."""
|
|
216
206
|
|
|
217
207
|
@staticmethod
|
|
218
208
|
def from_rrm_to_compressed_pixels(
|
|
@@ -243,7 +233,7 @@ class ImageData:
|
|
|
243
233
|
return compressed_pixels
|
|
244
234
|
|
|
245
235
|
@staticmethod
|
|
246
|
-
def
|
|
236
|
+
def _calculate_max_x_y(coord_array):
|
|
247
237
|
"""Calculate the max and min x and y coordinates."""
|
|
248
238
|
max_x = -float("inf")
|
|
249
239
|
max_y = -float("inf")
|
|
@@ -297,7 +287,7 @@ class ImageData:
|
|
|
297
287
|
except KeyError:
|
|
298
288
|
return None
|
|
299
289
|
predicted_path = ImageData.sublist_join(
|
|
300
|
-
|
|
290
|
+
RandImageData.rrm_valetudo_path_array(points), 2
|
|
301
291
|
)
|
|
302
292
|
return predicted_path
|
|
303
293
|
|
|
@@ -318,8 +308,17 @@ class ImageData:
|
|
|
318
308
|
Return the calculated angle and original angle.
|
|
319
309
|
"""
|
|
320
310
|
angle_c = round(json_data.get("robot_angle", 0))
|
|
321
|
-
|
|
322
|
-
|
|
311
|
+
# Convert negative values: -10 -> 350, -180 -> 359, but keep positive: 24 -> 24
|
|
312
|
+
if angle_c < 0:
|
|
313
|
+
if angle_c == -180:
|
|
314
|
+
angle = 359 # -180 becomes 359 (avoiding 360)
|
|
315
|
+
else:
|
|
316
|
+
angle = 360 + angle_c # -10 -> 350, -90 -> 270
|
|
317
|
+
else:
|
|
318
|
+
angle = angle_c
|
|
319
|
+
|
|
320
|
+
angle = (angle + 90) % 360
|
|
321
|
+
return angle, json_data.get("robot_angle", 0)
|
|
323
322
|
|
|
324
323
|
@staticmethod
|
|
325
324
|
def get_rrm_goto_target(json_data: JsonType) -> list or None:
|
|
@@ -330,7 +329,7 @@ class ImageData:
|
|
|
330
329
|
return None
|
|
331
330
|
|
|
332
331
|
if path_data and path_data != []:
|
|
333
|
-
path_data =
|
|
332
|
+
path_data = RandImageData.rrm_coordinates_to_valetudo(path_data)
|
|
334
333
|
return path_data
|
|
335
334
|
return None
|
|
336
335
|
|
|
@@ -338,18 +337,18 @@ class ImageData:
|
|
|
338
337
|
def get_rrm_currently_cleaned_zones(json_data: JsonType) -> dict:
|
|
339
338
|
"""Get the currently cleaned zones from the json."""
|
|
340
339
|
re_zones = json_data.get("currently_cleaned_zones", [])
|
|
341
|
-
formatted_zones =
|
|
340
|
+
formatted_zones = RandImageData._rrm_valetudo_format_zone(re_zones)
|
|
342
341
|
return formatted_zones
|
|
343
342
|
|
|
344
343
|
@staticmethod
|
|
345
344
|
def get_rrm_forbidden_zones(json_data: JsonType) -> dict:
|
|
346
345
|
"""Get the forbidden zones from the json."""
|
|
347
346
|
re_zones = json_data.get("forbidden_zones", [])
|
|
348
|
-
formatted_zones =
|
|
347
|
+
formatted_zones = RandImageData._rrm_valetudo_format_zone(re_zones)
|
|
349
348
|
return formatted_zones
|
|
350
349
|
|
|
351
350
|
@staticmethod
|
|
352
|
-
def
|
|
351
|
+
def _rrm_valetudo_format_zone(coordinates: list) -> any:
|
|
353
352
|
"""Format the zones from RRM to Valetudo."""
|
|
354
353
|
formatted_zones = []
|
|
355
354
|
for zone_data in coordinates:
|
|
@@ -391,7 +390,7 @@ class ImageData:
|
|
|
391
390
|
return formatted_zones
|
|
392
391
|
|
|
393
392
|
@staticmethod
|
|
394
|
-
def
|
|
393
|
+
def _rrm_valetudo_lines(coordinates: list) -> list:
|
|
395
394
|
"""Format the lines from RRM to Valetudo."""
|
|
396
395
|
formatted_lines = []
|
|
397
396
|
for lines in coordinates:
|
|
@@ -406,7 +405,7 @@ class ImageData:
|
|
|
406
405
|
tmp_data = json_data.get("virtual_walls", [])
|
|
407
406
|
except KeyError:
|
|
408
407
|
return None
|
|
409
|
-
virtual_walls =
|
|
408
|
+
virtual_walls = RandImageData._rrm_valetudo_lines(tmp_data)
|
|
410
409
|
return virtual_walls
|
|
411
410
|
|
|
412
411
|
@staticmethod
|
|
@@ -424,7 +423,7 @@ class ImageData:
|
|
|
424
423
|
"""Get the image size from the json."""
|
|
425
424
|
if isinstance(json_data, tuple):
|
|
426
425
|
return 0, 0
|
|
427
|
-
image =
|
|
426
|
+
image = RandImageData.get_rrm_image(json_data)
|
|
428
427
|
if image == {}:
|
|
429
428
|
return 0, 0
|
|
430
429
|
dimensions = image.get("dimensions", {})
|
|
@@ -433,20 +432,20 @@ class ImageData:
|
|
|
433
432
|
@staticmethod
|
|
434
433
|
def get_rrm_image_position(json_data: JsonType) -> tuple:
|
|
435
434
|
"""Get the image position from the json."""
|
|
436
|
-
image =
|
|
435
|
+
image = RandImageData.get_rrm_image(json_data)
|
|
437
436
|
position = image.get("position", {})
|
|
438
437
|
return position.get("top", 0), position.get("left", 0)
|
|
439
438
|
|
|
440
439
|
@staticmethod
|
|
441
440
|
def get_rrm_floor(json_data: JsonType) -> list:
|
|
442
441
|
"""Get the floor data from the json."""
|
|
443
|
-
img =
|
|
442
|
+
img = RandImageData.get_rrm_image(json_data)
|
|
444
443
|
return img.get("pixels", {}).get("floor", [])
|
|
445
444
|
|
|
446
445
|
@staticmethod
|
|
447
446
|
def get_rrm_walls(json_data: JsonType) -> list:
|
|
448
447
|
"""Get the walls data from the json."""
|
|
449
|
-
img =
|
|
448
|
+
img = RandImageData.get_rrm_image(json_data)
|
|
450
449
|
return img.get("pixels", {}).get("walls", [])
|
|
451
450
|
|
|
452
451
|
@staticmethod
|
|
@@ -460,7 +459,7 @@ class ImageData:
|
|
|
460
459
|
) -> tuple or list:
|
|
461
460
|
"""Get the segments data from the json."""
|
|
462
461
|
|
|
463
|
-
img =
|
|
462
|
+
img = RandImageData.get_rrm_image(json_data)
|
|
464
463
|
seg_data = img.get("segments", {})
|
|
465
464
|
seg_ids = seg_data.get("id")
|
|
466
465
|
segments = []
|
|
@@ -469,7 +468,7 @@ class ImageData:
|
|
|
469
468
|
for id_seg in seg_ids:
|
|
470
469
|
tmp_data = seg_data.get("pixels_seg_" + str(id_seg))
|
|
471
470
|
segments.append(
|
|
472
|
-
|
|
471
|
+
RandImageData.from_rrm_to_compressed_pixels(
|
|
473
472
|
tmp_data,
|
|
474
473
|
image_width=size_x,
|
|
475
474
|
image_height=size_y,
|
|
@@ -493,17 +492,8 @@ class ImageData:
|
|
|
493
492
|
def get_rrm_segments_ids(json_data: JsonType) -> list or None:
|
|
494
493
|
"""Get the segments ids from the json."""
|
|
495
494
|
try:
|
|
496
|
-
img =
|
|
495
|
+
img = RandImageData.get_rrm_image(json_data)
|
|
497
496
|
seg_ids = img.get("segments", {}).get("id", [])
|
|
498
497
|
except KeyError:
|
|
499
498
|
return None
|
|
500
499
|
return seg_ids
|
|
501
|
-
|
|
502
|
-
@staticmethod
|
|
503
|
-
def convert_negative_angle(angle: int) -> int:
|
|
504
|
-
"""Convert negative angle to positive."""
|
|
505
|
-
angle_c = angle % 360 # Ensure angle is within 0-359
|
|
506
|
-
if angle_c < 0:
|
|
507
|
-
angle_c += 360 # Convert negative angle to positive
|
|
508
|
-
angle = angle_c + 180 # add offset
|
|
509
|
-
return angle
|