valetudo-map-parser 0.1.4__py3-none-any.whl → 0.1.6__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.
@@ -0,0 +1,372 @@
1
+ """
2
+ Image Draw Class for Valetudo Rand256 Image Handling.
3
+ This class is used to simplify the ImageHandler class.
4
+ Version: 2024.12.0
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import hashlib
10
+ import json
11
+ import logging
12
+
13
+ from .config.types import Color, JsonType, NumpyArray
14
+ from .config.drawable import Drawable
15
+ from map_data import ImageData
16
+
17
+ _LOGGER = logging.getLogger(__name__)
18
+
19
+
20
+ class ImageDraw:
21
+ """Class to handle the image creation."""
22
+
23
+ """It Draws each elements of the images, like the walls, zones, paths, etc."""
24
+
25
+ def __init__(self, image_handler):
26
+ self.img_h = image_handler
27
+ self.file_name = self.img_h.shared.file_name
28
+ self.data = ImageData
29
+ self.draw = Drawable
30
+ self.color_grey = (128, 128, 128, 255)
31
+
32
+ async def async_draw_go_to_flag(
33
+ self, np_array: NumpyArray, m_json: JsonType, color_go_to: Color
34
+ ) -> NumpyArray:
35
+ """Draw the goto target flag on the map."""
36
+ try:
37
+ go_to = self.data.get_rrm_goto_target(m_json)
38
+ if go_to:
39
+ np_array = await self.draw.go_to_flag(
40
+ np_array,
41
+ (go_to[0], go_to[1]),
42
+ self.img_h.img_rotate,
43
+ color_go_to,
44
+ )
45
+ predicted_path = self.data.get_rrm_goto_predicted_path(m_json)
46
+ if predicted_path:
47
+ np_array = await self.draw.lines(
48
+ np_array, predicted_path, 3, self.color_grey
49
+ )
50
+ return np_array
51
+ else:
52
+ return np_array
53
+ except Exception as e:
54
+ _LOGGER.warning(
55
+ f"{self.file_name}: Error in extraction of go to. {e}", exc_info=True
56
+ )
57
+ return np_array
58
+
59
+ async def async_segment_data(
60
+ self, m_json, size_x, size_y, pos_top, pos_left
61
+ ) -> None:
62
+ """Get the segments data from the JSON data."""
63
+ try:
64
+ if not self.img_h.segment_data:
65
+ (
66
+ self.img_h.segment_data,
67
+ self.img_h.outlines,
68
+ ) = await self.data.async_get_rrm_segments(
69
+ m_json, size_x, size_y, pos_top, pos_left, True
70
+ )
71
+ except ValueError as e:
72
+ self.img_h.segment_data = None
73
+ _LOGGER.info(f"{self.file_name}: No segments data found. {e}")
74
+
75
+ async def async_draw_base_layer(
76
+ self,
77
+ m_json,
78
+ size_x,
79
+ size_y,
80
+ color_wall,
81
+ color_zone_clean,
82
+ color_background,
83
+ pixel_size,
84
+ ):
85
+ """Draw the base layer of the map."""
86
+ pos_top, pos_left = self.data.get_rrm_image_position(m_json)
87
+ walls_data = self.data.get_rrm_walls(m_json)
88
+ floor_data = self.data.get_rrm_floor(m_json)
89
+
90
+ _LOGGER.info(self.file_name + ": Empty image with background color")
91
+ img_np_array = await self.draw.create_empty_image(
92
+ self.img_h.img_size["x"], self.img_h.img_size["y"], color_background
93
+ )
94
+ room_id = 0
95
+ if self.img_h.frame_number == 0:
96
+ _LOGGER.info(self.file_name + ": Overlapping Layers")
97
+
98
+ # checking if there are segments too (sorted pixels in the raw data).
99
+ await self.async_segment_data(m_json, size_x, size_y, pos_top, pos_left)
100
+
101
+ img_np_array = await self._draw_floor(
102
+ img_np_array, floor_data, size_x, size_y, pos_top, pos_left, pixel_size
103
+ )
104
+ room_id, img_np_array = await self._draw_segments(
105
+ img_np_array,
106
+ pixel_size,
107
+ self.img_h.segment_data,
108
+ color_wall,
109
+ color_zone_clean,
110
+ )
111
+ img_np_array = await self._draw_walls(
112
+ img_np_array,
113
+ walls_data,
114
+ size_x,
115
+ size_y,
116
+ pos_top,
117
+ pos_left,
118
+ pixel_size,
119
+ color_wall,
120
+ )
121
+ return room_id, img_np_array
122
+
123
+ async def _draw_floor(
124
+ self, img_np_array, floor_data, size_x, size_y, pos_top, pos_left, pixel_size
125
+ ):
126
+ """Draw the floor data onto the image."""
127
+ pixels = self.data.from_rrm_to_compressed_pixels(
128
+ floor_data,
129
+ image_width=size_x,
130
+ image_height=size_y,
131
+ image_top=pos_top,
132
+ image_left=pos_left,
133
+ )
134
+ if pixels:
135
+ room_color = self.img_h.shared.rooms_colors[0] # Using initial room_id = 0
136
+ img_np_array = await self.draw.from_json_to_image(
137
+ img_np_array, pixels, pixel_size, room_color
138
+ )
139
+ return img_np_array
140
+
141
+ async def _draw_segments(
142
+ self, img_np_array, pixel_size, segment_data, color_wall, color_zone_clean
143
+ ):
144
+ """Draw the segments onto the image and update room_id."""
145
+
146
+ room_id = 0
147
+ rooms_list = [color_wall]
148
+ if not segment_data:
149
+ _LOGGER.info(f"{self.file_name}: No segments data found.")
150
+ return room_id, img_np_array
151
+
152
+ if segment_data:
153
+ _LOGGER.info(f"{self.file_name}: Drawing segments.")
154
+ for pixels in segment_data:
155
+ room_color = self.img_h.shared.rooms_colors[room_id]
156
+ rooms_list.append(room_color)
157
+ if (
158
+ self.img_h.active_zones
159
+ and len(self.img_h.active_zones) > room_id
160
+ and self.img_h.active_zones[room_id] == 1
161
+ ):
162
+ room_color = (
163
+ ((2 * room_color[0]) + color_zone_clean[0]) // 3,
164
+ ((2 * room_color[1]) + color_zone_clean[1]) // 3,
165
+ ((2 * room_color[2]) + color_zone_clean[2]) // 3,
166
+ ((2 * room_color[3]) + color_zone_clean[3]) // 3,
167
+ )
168
+ img_np_array = await self.draw.from_json_to_image(
169
+ img_np_array, pixels, pixel_size, room_color
170
+ )
171
+ room_id += 1
172
+ if room_id > 15:
173
+ room_id = 0
174
+ return room_id, img_np_array
175
+
176
+ async def _draw_walls(
177
+ self,
178
+ img_np_array,
179
+ walls_data,
180
+ size_x,
181
+ size_y,
182
+ pos_top,
183
+ pos_left,
184
+ pixel_size,
185
+ color_wall,
186
+ ):
187
+ """Draw the walls onto the image."""
188
+ walls = self.data.from_rrm_to_compressed_pixels(
189
+ walls_data,
190
+ image_width=size_x,
191
+ image_height=size_y,
192
+ image_left=pos_left,
193
+ image_top=pos_top,
194
+ )
195
+ if walls:
196
+ img_np_array = await self.draw.from_json_to_image(
197
+ img_np_array, walls, pixel_size, color_wall
198
+ )
199
+ return img_np_array
200
+
201
+ async def async_draw_charger(
202
+ self,
203
+ np_array: NumpyArray,
204
+ m_json: JsonType,
205
+ color_charger: Color,
206
+ ) -> (NumpyArray, dict):
207
+ """Get the charger position from the entity data."""
208
+ try:
209
+ charger_pos = self.data.rrm_coordinates_to_valetudo(
210
+ self.data.get_rrm_charger_position(m_json)
211
+ )
212
+ except Exception as e:
213
+ _LOGGER.warning(f"{self.file_name}: No charger position found. {e}")
214
+ else:
215
+ _LOGGER.debug("charger position: %s", charger_pos)
216
+ if charger_pos:
217
+ charger_pos_dictionary = {
218
+ "x": (charger_pos[0] * 10),
219
+ "y": (charger_pos[1] * 10),
220
+ }
221
+
222
+ np_array = await self.draw.battery_charger(
223
+ np_array, charger_pos[0], charger_pos[1], color_charger
224
+ )
225
+ return np_array, charger_pos_dictionary
226
+ else:
227
+ return np_array, {}
228
+
229
+ async def async_draw_zones(
230
+ self,
231
+ m_json: JsonType,
232
+ np_array: NumpyArray,
233
+ color_zone_clean: Color,
234
+ ) -> NumpyArray:
235
+ """Get the zone clean from the JSON data."""
236
+ try:
237
+ zone_clean = self.data.get_rrm_currently_cleaned_zones(m_json)
238
+ except (ValueError, KeyError):
239
+ zone_clean = None
240
+ else:
241
+ _LOGGER.info(f"{self.file_name}: Got zones.")
242
+ if zone_clean:
243
+ return await self.draw.zones(np_array, zone_clean, color_zone_clean)
244
+ else:
245
+ return np_array
246
+
247
+ async def async_draw_virtual_restrictions(
248
+ self, m_json: JsonType, np_array: NumpyArray, color_no_go: Color
249
+ ) -> NumpyArray:
250
+ """Get the virtual walls from the JSON data."""
251
+ try:
252
+ virtual_walls = self.data.get_rrm_virtual_walls(m_json)
253
+ except (ValueError, KeyError):
254
+ virtual_walls = None
255
+ else:
256
+ _LOGGER.info(f"{self.file_name}: Got virtual walls.")
257
+ if virtual_walls:
258
+ np_array = await self.draw.draw_virtual_walls(
259
+ np_array, virtual_walls, color_no_go
260
+ )
261
+ try:
262
+ no_go_area = self.data.get_rrm_forbidden_zones(m_json)
263
+ except KeyError:
264
+ no_go_area = None
265
+ if no_go_area:
266
+ np_array = await self.draw.zones(np_array, no_go_area, color_no_go)
267
+ return np_array
268
+
269
+ async def async_draw_path(
270
+ self,
271
+ np_array: NumpyArray,
272
+ m_json: JsonType,
273
+ color_move: Color,
274
+ ) -> NumpyArray:
275
+ """Get the paths from the JSON data."""
276
+ # Initialize the variables
277
+ path_pixel_formatted = None
278
+ # Extract the paths data from the JSON data.
279
+ try:
280
+ path_pixel = self.data.get_rrm_path(m_json)
281
+ path_pixel_formatted = self.data.sublist_join(
282
+ self.data.rrm_valetudo_path_array(path_pixel["points"]), 2
283
+ )
284
+ except KeyError as e:
285
+ _LOGGER.warning(f"{self.file_name}: Error extracting paths data: {str(e)}")
286
+ finally:
287
+ if path_pixel_formatted:
288
+ np_array = await self.draw.lines(
289
+ np_array, path_pixel_formatted, 5, color_move
290
+ )
291
+ return np_array
292
+
293
+ async def async_get_entity_data(self, m_json: JsonType) -> dict or None:
294
+ """Get the entity data from the JSON data."""
295
+ try:
296
+ entity_dict = self.data.find_points_entities(m_json)
297
+ except (ValueError, KeyError):
298
+ entity_dict = None
299
+ else:
300
+ _LOGGER.info(f"{self.file_name}: Got the points in the json.")
301
+ return entity_dict
302
+
303
+ @staticmethod
304
+ async def async_copy_array(original_array: NumpyArray) -> NumpyArray:
305
+ """Copy the array."""
306
+ return NumpyArray.copy(original_array)
307
+
308
+ async def calculate_array_hash(self, layers: dict, active: list[int] = None) -> str:
309
+ """Calculate the hash of the image based on the layers and active segments walls."""
310
+ self.img_h.active_zones = active
311
+ if layers and active:
312
+ data_to_hash = {
313
+ "layers": len(layers["wall"][0]),
314
+ "active_segments": tuple(active),
315
+ }
316
+ data_json = json.dumps(data_to_hash, sort_keys=True)
317
+ hash_value = hashlib.sha256(data_json.encode()).hexdigest()
318
+ else:
319
+ hash_value = None
320
+ return hash_value
321
+
322
+ async def async_get_robot_position(self, m_json: JsonType) -> tuple | None:
323
+ """Get the robot position from the entity data."""
324
+ robot_pos = None
325
+ robot_position = None
326
+ angle = [0, 0]
327
+ try:
328
+ robot_pos_data = self.data.get_rrm_robot_position(m_json)
329
+ robot_pos = self.data.rrm_coordinates_to_valetudo(robot_pos_data)
330
+ angle = self.data.get_rrm_robot_angle(m_json)
331
+ except (ValueError, KeyError):
332
+ _LOGGER.warning(f"{self.file_name} No robot position found.")
333
+ return None, None, None
334
+ finally:
335
+ robot_position_angle = round(angle[0], 0)
336
+ if robot_pos and robot_position_angle:
337
+ robot_position = robot_pos
338
+ _LOGGER.debug(
339
+ f"robot position: {robot_pos}, robot angle: {robot_position_angle}"
340
+ )
341
+ if self.img_h.rooms_pos is None:
342
+ self.img_h.robot_pos = {
343
+ "x": robot_position[0] * 10,
344
+ "y": robot_position[1] * 10,
345
+ "angle": robot_position_angle,
346
+ }
347
+ else:
348
+ self.img_h.robot_pos = await self.img_h.async_get_robot_in_room(
349
+ (robot_position[0] * 10),
350
+ (robot_position[1] * 10),
351
+ robot_position_angle,
352
+ )
353
+ return robot_pos, robot_position, robot_position_angle
354
+
355
+ async def async_draw_robot_on_map(
356
+ self,
357
+ np_array: NumpyArray,
358
+ robot_pos: tuple,
359
+ robot_angle: float,
360
+ color_robot: Color,
361
+ ) -> NumpyArray:
362
+ """Draw the robot on the map."""
363
+ if robot_pos and robot_angle:
364
+ np_array = await self.draw.robot(
365
+ layers=np_array,
366
+ x=robot_pos[0],
367
+ y=robot_pos[1],
368
+ angle=robot_angle,
369
+ fill=color_robot,
370
+ robot_state=self.img_h.shared.vacuum_state,
371
+ )
372
+ return np_array
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: valetudo-map-parser
3
- Version: 0.1.4
3
+ Version: 0.1.6
4
4
  Summary: A Python library to parse Valetudo map data returning a PIL Image object.
5
5
  License: Apache-2.0
6
6
  Author: Sandro Cantarella
@@ -0,0 +1,20 @@
1
+ valetudo_map_parser/__init__.py,sha256=9eWCgcSvmRe_MWEInCAjXkF9LspOuCWesjr4eUtsdA8,790
2
+ valetudo_map_parser/config/__init__.py,sha256=DQ9plV3ZF_K25Dp5ZQHPDoG-40dQoJNdNi-dfNeR3Zc,48
3
+ valetudo_map_parser/config/auto_crop.py,sha256=vaDAVVn86agrlEjX-rjYZxDtAZ7P92rWqF1nSyT2kJA,11014
4
+ valetudo_map_parser/config/colors.py,sha256=HlgROKagpV7FP9LTU1IvhTKTYjnlBw1ZDMKHVJSct8M,6514
5
+ valetudo_map_parser/config/drawable.py,sha256=8Fp4yqyFbsGvNyCEynYCTpiohRZk_plUSSOyHpECvj8,20356
6
+ valetudo_map_parser/config/rand25_parser.py,sha256=vkghj60Bdq22P3UnLCkKc77mqyJP524z2O9k8NvEi6M,15806
7
+ valetudo_map_parser/config/shared.py,sha256=8xYBUfKY-tiPhXaT5ttIasnCWL3_RLawIZeTValQC64,9462
8
+ valetudo_map_parser/config/types.py,sha256=bVSEDE0ihrc01jG4fZ1_hUVtoj6hdkbqShytZ6wJwJY,16163
9
+ valetudo_map_parser/hypfer_draw.py,sha256=R8JVrqPPavhj8exCDJKk1QsXgsB12cjy4AIgFffyQec,15773
10
+ valetudo_map_parser/hypfer_handler.py,sha256=MoCO4zVflFd0HSB4IRz1EOjJtlMfaagJS7A-OqJUNIU,18457
11
+ valetudo_map_parser/images_utils.py,sha256=Ls5OnXXcojBTuUagj3_N4tY3nOe9aot7gl5hrPCXKss,15262
12
+ valetudo_map_parser/map_data.py,sha256=yt_osaewoYjcQPV1u6R_N801XmAH8EgR7mKrsbOcph0,19449
13
+ valetudo_map_parser/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
14
+ valetudo_map_parser/rand25_handler.py,sha256=pwbMEa4ZLtS3hsja2lC2D0zpa76mKWQz4mQ5yBJ-sa0,19872
15
+ valetudo_map_parser/reimg_draw.py,sha256=zs-HHncsFxV5kozMqZ4XCYRJx8anQ3K0I440RPt7ADI,13404
16
+ valetudo_map_parser-0.1.6.dist-info/LICENSE,sha256=Lh-qBbuRV0-jiCIBhfV7NgdwFxQFOXH3BKOzK865hRs,10480
17
+ valetudo_map_parser-0.1.6.dist-info/METADATA,sha256=ABBsZ58Yz7Pp0ZnlOU9oX9EexC23iBS-dHV7QTnXmpw,1026
18
+ valetudo_map_parser-0.1.6.dist-info/NOTICE.txt,sha256=5lTOuWiU9aiEnJ2go8sc7lTJ7ntMBx0g0GFnNrswCY4,2533
19
+ valetudo_map_parser-0.1.6.dist-info/WHEEL,sha256=RaoafKOydTQ7I_I3JTrPCg6kUmTgtm4BornzOqyEfJ8,88
20
+ valetudo_map_parser-0.1.6.dist-info/RECORD,,
@@ -1,17 +0,0 @@
1
- valetudo_map_parser/__init__.py,sha256=SYv1w4Zf6ADLJ6yC6W8vhzQ96DicDRSP3yuf6ijmlxc,683
2
- valetudo_map_parser/config/__init__.py,sha256=DQ9plV3ZF_K25Dp5ZQHPDoG-40dQoJNdNi-dfNeR3Zc,48
3
- valetudo_map_parser/config/auto_crop.py,sha256=eTzMVhaeP7SP2-65jefE4toAigkZM6XUoARsSB5JwH8,10927
4
- valetudo_map_parser/config/colors.py,sha256=HlgROKagpV7FP9LTU1IvhTKTYjnlBw1ZDMKHVJSct8M,6514
5
- valetudo_map_parser/config/drawable.py,sha256=8Fp4yqyFbsGvNyCEynYCTpiohRZk_plUSSOyHpECvj8,20356
6
- valetudo_map_parser/config/shared.py,sha256=8xYBUfKY-tiPhXaT5ttIasnCWL3_RLawIZeTValQC64,9462
7
- valetudo_map_parser/config/types.py,sha256=bVSEDE0ihrc01jG4fZ1_hUVtoj6hdkbqShytZ6wJwJY,16163
8
- valetudo_map_parser/hypfer_draw.py,sha256=R8JVrqPPavhj8exCDJKk1QsXgsB12cjy4AIgFffyQec,15773
9
- valetudo_map_parser/hypfer_handler.py,sha256=PCdfzLpGZD0zXvVufFWadPc-xDqbtsnArfPW0PWViU4,18469
10
- valetudo_map_parser/images_utils.py,sha256=Ls5OnXXcojBTuUagj3_N4tY3nOe9aot7gl5hrPCXKss,15262
11
- valetudo_map_parser/map_data.py,sha256=-6etLFZXZikFEUeR2YhPLgXu7dv3YRY2e-u5Mx9ogqw,19498
12
- valetudo_map_parser/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
13
- valetudo_map_parser-0.1.4.dist-info/LICENSE,sha256=Lh-qBbuRV0-jiCIBhfV7NgdwFxQFOXH3BKOzK865hRs,10480
14
- valetudo_map_parser-0.1.4.dist-info/METADATA,sha256=qsrU3YyxUwjsKvqo6IReZAneO_6KIAVHVvdVDLYlTMA,1026
15
- valetudo_map_parser-0.1.4.dist-info/NOTICE.txt,sha256=5lTOuWiU9aiEnJ2go8sc7lTJ7ntMBx0g0GFnNrswCY4,2533
16
- valetudo_map_parser-0.1.4.dist-info/WHEEL,sha256=RaoafKOydTQ7I_I3JTrPCg6kUmTgtm4BornzOqyEfJ8,88
17
- valetudo_map_parser-0.1.4.dist-info/RECORD,,