valetudo-map-parser 0.1.9b44__py3-none-any.whl → 0.1.9b46__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.
@@ -26,11 +26,8 @@ from .config.types import (
26
26
  )
27
27
  from .config.utils import (
28
28
  BaseHandler,
29
- blend_colors,
30
- blend_pixel,
31
29
  get_element_at_position,
32
30
  get_room_at_position,
33
- handle_room_outline_error,
34
31
  initialize_drawing_config,
35
32
  manage_drawable_elements,
36
33
  prepare_resize_params,
@@ -66,15 +63,15 @@ class HypferMapImageHandler(BaseHandler, AutoCrop):
66
63
  self.imd = ImDraw(self) # Image Draw class.
67
64
  self.color_grey = (128, 128, 128, 255)
68
65
  self.file_name = self.shared.file_name # file name of the vacuum.
69
- self.element_map_manager = OptimizedElementMapGenerator(self.drawing_config,
70
- self.shared) # Map of element codes
66
+ self.element_map_manager = OptimizedElementMapGenerator(
67
+ self.drawing_config, self.shared
68
+ ) # Map of element codes
71
69
 
72
70
  @staticmethod
73
71
  def get_corners(x_max, x_min, y_max, y_min):
74
72
  """Get the corners of the room."""
75
73
  return [(x_min, y_min), (x_max, y_min), (x_max, y_max), (x_min, y_max)]
76
74
 
77
-
78
75
  async def extract_room_outline_from_map(self, room_id_int, pixels, pixel_size):
79
76
  """Extract the outline of a room using the pixel data and element map.
80
77
 
@@ -159,26 +156,7 @@ class HypferMapImageHandler(BaseHandler, AutoCrop):
159
156
  x_max,
160
157
  y_max,
161
158
  ) = await self.data.async_get_rooms_coordinates(pixels, pixel_size)
162
-
163
- # Get rectangular corners as a fallback
164
159
  corners = self.get_corners(x_max, x_min, y_max, y_min)
165
-
166
- # Try to extract a more accurate room outline from the element map
167
- try:
168
- # Extract the room outline using the element map
169
- outline = await self.extract_room_outline_from_map(
170
- segment_id, pixels, pixel_size
171
- )
172
- LOGGER.debug(
173
- "%s: Traced outline for room %s with %d points",
174
- self.file_name,
175
- segment_id,
176
- len(outline),
177
- )
178
- except (ValueError, IndexError, TypeError, ArithmeticError) as e:
179
- handle_room_outline_error(self.file_name, segment_id, e)
180
- outline = corners
181
-
182
160
  room_id = str(segment_id)
183
161
  self.rooms_pos.append(
184
162
  {
@@ -188,7 +166,7 @@ class HypferMapImageHandler(BaseHandler, AutoCrop):
188
166
  )
189
167
  room_properties[room_id] = {
190
168
  "number": segment_id,
191
- "outline": outline, # Use the detailed outline from the element map
169
+ "outline": corners,
192
170
  "name": name,
193
171
  "x": ((x_min + x_max) // 2),
194
172
  "y": ((y_min + y_max) // 2),
@@ -254,7 +232,6 @@ class HypferMapImageHandler(BaseHandler, AutoCrop):
254
232
  size_x, size_y, colors["background"]
255
233
  )
256
234
 
257
-
258
235
  LOGGER.info("%s: Drawing map with color blending", self.file_name)
259
236
 
260
237
  # Draw layers and segments if enabled
@@ -473,9 +450,14 @@ class HypferMapImageHandler(BaseHandler, AutoCrop):
473
450
  )
474
451
 
475
452
  # Update element map for robot position
476
- if hasattr(self.shared, 'element_map') and self.shared.element_map is not None:
453
+ if (
454
+ hasattr(self.shared, "element_map")
455
+ and self.shared.element_map is not None
456
+ ):
477
457
  update_element_map_with_robot(
478
- self.shared.element_map, robot_position, DrawableElement.ROBOT
458
+ self.shared.element_map,
459
+ robot_position,
460
+ DrawableElement.ROBOT,
479
461
  )
480
462
  # Resize the image
481
463
  img_np_array = await self.async_auto_trim_and_zoom_image(
@@ -490,21 +472,35 @@ class HypferMapImageHandler(BaseHandler, AutoCrop):
490
472
  LOGGER.warning("%s: Image array is None.", self.file_name)
491
473
  return None
492
474
  # Debug logging for element map creation
493
- LOGGER.info("%s: Frame number: %d, has element_map: %s",
494
- self.file_name, self.frame_number, hasattr(self.shared, 'element_map'))
475
+ LOGGER.info(
476
+ "%s: Frame number: %d, has element_map: %s",
477
+ self.file_name,
478
+ self.frame_number,
479
+ hasattr(self.shared, "element_map"),
480
+ )
495
481
 
496
482
  if (self.shared.element_map is None) and (self.frame_number == 1):
497
483
  # Create element map for tracking what's drawn where
498
- LOGGER.info("%s: Creating element map with shape: %s",
499
- self.file_name, img_np_array.shape)
484
+ LOGGER.info(
485
+ "%s: Creating element map with shape: %s",
486
+ self.file_name,
487
+ img_np_array.shape,
488
+ )
500
489
 
501
490
  # Generate the element map directly from JSON data
502
491
  # This will create a cropped element map containing only the non-zero elements
503
492
  LOGGER.info("%s: Generating element map from JSON data", self.file_name)
504
- self.shared.element_map = await self.element_map_manager.async_generate_from_json(m_json)
493
+ self.shared.element_map = (
494
+ await self.element_map_manager.async_generate_from_json(m_json)
495
+ )
505
496
 
506
- LOGGER.info("%s: Element map created with shape: %s",
507
- self.file_name, self.shared.element_map.shape if self.shared.element_map is not None else None)
497
+ LOGGER.info(
498
+ "%s: Element map created with shape: %s",
499
+ self.file_name,
500
+ self.shared.element_map.shape
501
+ if self.shared.element_map is not None
502
+ else None,
503
+ )
508
504
  # Convert the numpy array to a PIL image
509
505
  pil_img = Image.fromarray(img_np_array, mode="RGBA")
510
506
  del img_np_array
@@ -595,29 +591,8 @@ class HypferMapImageHandler(BaseHandler, AutoCrop):
595
591
 
596
592
  def get_room_at_position(self, x: int, y: int) -> int | None:
597
593
  """Get the room ID at a specific position, or None if not a room."""
598
- return get_room_at_position(self.shared.element_map, x, y, DrawableElement.ROOM_1)
599
-
600
- @staticmethod
601
- def blend_colors(base_color, overlay_color):
602
- """
603
- Blend two RGBA colors, considering alpha channels.
604
-
605
- Args:
606
- base_color: The base RGBA color
607
- overlay_color: The overlay RGBA color to blend on top
608
-
609
- Returns:
610
- The blended RGBA color
611
- """
612
- return blend_colors(base_color, overlay_color)
613
-
614
- def blend_pixel(self, array, x, y, color, element):
615
- """
616
- Blend a pixel color with the existing color at the specified position.
617
- Also updates the element map if the new element has higher z-index.
618
- """
619
- return blend_pixel(
620
- array, x, y, color, element, self.shared.element_map, self.drawing_config
594
+ return get_room_at_position(
595
+ self.shared.element_map, x, y, DrawableElement.ROOM_1
621
596
  )
622
597
 
623
598
  @staticmethod
@@ -3,7 +3,7 @@ Collections of Json and List routines
3
3
  ImageData is part of the Image_Handler
4
4
  used functions to search data in the json
5
5
  provided for the creation of the new camera frame
6
- Version: v0.1.9.b42
6
+ Version: v0.1.6
7
7
  """
8
8
 
9
9
  from __future__ import annotations
@@ -31,7 +31,6 @@ from .config.utils import (
31
31
  BaseHandler,
32
32
  get_element_at_position,
33
33
  get_room_at_position,
34
- handle_room_outline_error,
35
34
  initialize_drawing_config,
36
35
  manage_drawable_elements,
37
36
  prepare_resize_params,
@@ -150,81 +149,55 @@ class ReImageHandler(BaseHandler, AutoCrop):
150
149
  room_id_to_data = {room["id"]: room for room in room_data}
151
150
  self.rooms_pos = []
152
151
  room_properties = {}
153
- if not self.outlines:
154
- # Return empty data if no outlines are available
155
- _LOGGER.debug("%s: No outlines available", self.file_name)
156
- return None, None, None
157
-
158
- # If we have outlines, proceed with processing
159
- for id_x, room_id in enumerate(unsorted_id):
160
- if room_id in room_id_to_data:
161
- room_info = room_id_to_data[room_id]
162
- name = room_info.get("name")
163
- # Calculate x and y min/max from outlines
164
- x_min = self.outlines[id_x][0][0]
165
- x_max = self.outlines[id_x][1][0]
166
- y_min = self.outlines[id_x][0][1]
167
- y_max = self.outlines[id_x][1][1]
168
-
169
- # Get rectangular corners as a fallback
170
- corners = self.get_corners(x_max, x_min, y_max, y_min)
171
-
172
- # Try to extract a more accurate room outline from the element map
173
- try:
174
- # Extract the room outline using the element map
175
- outline = await self.extract_room_outline_from_map(
176
- room_id, self.segment_data[id_x]
152
+ if self.outlines:
153
+ for id_x, room_id in enumerate(unsorted_id):
154
+ if room_id in room_id_to_data:
155
+ room_info = room_id_to_data[room_id]
156
+ name = room_info.get("name")
157
+ # Calculate x and y min/max from outlines
158
+ x_min = self.outlines[id_x][0][0]
159
+ x_max = self.outlines[id_x][1][0]
160
+ y_min = self.outlines[id_x][0][1]
161
+ y_max = self.outlines[id_x][1][1]
162
+ corners = self.get_corners(x_max, x_min, y_max, y_min)
163
+ # rand256 vacuums accept int(room_id) or str(name)
164
+ # the card will soon support int(room_id) but the camera will send name
165
+ # this avoids the manual change of the values in the card.
166
+ self.rooms_pos.append(
167
+ {
168
+ "name": name,
169
+ "corners": corners,
170
+ }
177
171
  )
178
- _LOGGER.debug(
179
- "%s: Traced outline for room %s with %d points",
180
- self.file_name,
181
- room_id,
182
- len(outline),
183
- )
184
- except (
185
- ValueError,
186
- IndexError,
187
- TypeError,
188
- ArithmeticError,
189
- ) as e:
190
- handle_room_outline_error(self.file_name, room_id, e)
191
- outline = corners
192
-
193
- # rand256 vacuums accept int(room_id) or str(name)
194
- # the card will soon support int(room_id) but the camera will send name
195
- # this avoids the manual change of the values in the card.
196
- self.rooms_pos.append(
197
- {
172
+ room_properties[int(room_id)] = {
173
+ "number": int(room_id),
174
+ "outline": corners,
198
175
  "name": name,
199
- "corners": corners,
176
+ "x": (x_min + x_max) // 2,
177
+ "y": (y_min + y_max) // 2,
200
178
  }
179
+ # get the zones and points data
180
+ zone_properties = await self.async_zone_propriety(zones_data)
181
+ # get the points data
182
+ point_properties = await self.async_points_propriety(points_data)
183
+ if room_properties or zone_properties:
184
+ extracted_data = [
185
+ f"{len(room_properties)} Rooms" if room_properties else None,
186
+ f"{len(zone_properties)} Zones" if zone_properties else None,
187
+ ]
188
+ extracted_data = ", ".join(filter(None, extracted_data))
189
+ _LOGGER.debug("Extracted data: %s", extracted_data)
190
+ else:
191
+ self.rooms_pos = None
192
+ _LOGGER.debug(
193
+ "%s: Rooms and Zones data not available!", self.file_name
201
194
  )
202
- room_properties[int(room_id)] = {
203
- "number": int(room_id),
204
- "outline": outline,
205
- "name": name,
206
- "x": (x_min + x_max) // 2,
207
- "y": (y_min + y_max) // 2,
208
- }
209
- # get the zones and points data
210
- zone_properties = await self.async_zone_propriety(zones_data)
211
- # get the points data
212
- point_properties = await self.async_points_propriety(points_data)
213
- if room_properties or zone_properties:
214
- extracted_data = [
215
- f"{len(room_properties)} Rooms" if room_properties else None,
216
- f"{len(zone_properties)} Zones" if zone_properties else None,
217
- ]
218
- extracted_data = ", ".join(filter(None, extracted_data))
219
- _LOGGER.debug("Extracted data: %s", extracted_data)
195
+ rooms = RoomStore(self.file_name, room_properties)
196
+ _LOGGER.debug("Rooms Data: %s", rooms.get_rooms())
197
+ return room_properties, zone_properties, point_properties
220
198
  else:
221
- self.rooms_pos = None
222
- _LOGGER.debug(
223
- "%s: Rooms and Zones data not available!", self.file_name
224
- )
225
- rooms = RoomStore(self.file_name, room_properties)
226
- _LOGGER.debug("Rooms Data: %s", rooms.get_rooms())
227
- return room_properties, zone_properties, point_properties
199
+ _LOGGER.debug("%s: No outlines available", self.file_name)
200
+ return None, None, None
228
201
  except (RuntimeError, ValueError) as e:
229
202
  _LOGGER.debug(
230
203
  "No rooms Data or Error in extract_room_properties: %s",
@@ -427,7 +400,7 @@ class ReImageHandler(BaseHandler, AutoCrop):
427
400
 
428
401
  async def get_rooms_attributes(
429
402
  self, destinations: JsonType = None
430
- ) -> RoomsProperties:
403
+ ) -> tuple[RoomsProperties, Any, Any]:
431
404
  """Return the rooms attributes."""
432
405
  if self.room_propriety:
433
406
  return self.room_propriety
@@ -475,12 +448,7 @@ class ReImageHandler(BaseHandler, AutoCrop):
475
448
  # If no rooms data is available, return a default position
476
449
  if not self.rooms_pos:
477
450
  _LOGGER.debug("%s: No rooms data available", self.file_name)
478
- return {
479
- "x": robot_x,
480
- "y": robot_y,
481
- "angle": angle,
482
- "in_room": "unknown"
483
- }
451
+ return {"x": robot_x, "y": robot_y, "angle": angle, "in_room": "unknown"}
484
452
 
485
453
  # If rooms data is available, search for the room
486
454
  if self.robot_in_room:
@@ -504,9 +472,7 @@ class ReImageHandler(BaseHandler, AutoCrop):
504
472
  "angle": angle,
505
473
  "in_room": self.robot_in_room["room"],
506
474
  }
507
- _LOGGER.debug(
508
- "%s is in %s", self.file_name, self.robot_in_room["room"]
509
- )
475
+ _LOGGER.debug("%s is in %s", self.file_name, self.robot_in_room["room"])
510
476
  del room, corners, robot_x, robot_y # free memory.
511
477
  return temp
512
478
  # After checking all rooms and not finding a match
@@ -586,8 +552,16 @@ class ReImageHandler(BaseHandler, AutoCrop):
586
552
 
587
553
  charger_radius = 15
588
554
  # Handle both dictionary format {'x': x, 'y': y} and list format [x, y]
589
- charger_x = self.charger_pos.get('x') if isinstance(self.charger_pos, dict) else self.charger_pos[0]
590
- charger_y = self.charger_pos.get('y') if isinstance(self.charger_pos, dict) else self.charger_pos[1]
555
+ charger_x = (
556
+ self.charger_pos.get("x")
557
+ if isinstance(self.charger_pos, dict)
558
+ else self.charger_pos[0]
559
+ )
560
+ charger_y = (
561
+ self.charger_pos.get("y")
562
+ if isinstance(self.charger_pos, dict)
563
+ else self.charger_pos[1]
564
+ )
591
565
 
592
566
  for dy in range(-charger_radius, charger_radius + 1):
593
567
  for dx in range(-charger_radius, charger_radius + 1):
@@ -595,5 +569,7 @@ class ReImageHandler(BaseHandler, AutoCrop):
595
569
  if dx * dx + dy * dy <= charger_radius * charger_radius:
596
570
  cx, cy = int(charger_x + dx), int(charger_y + dy)
597
571
  # Check if the coordinates are within the element map bounds
598
- if (0 <= cy < self.element_map.shape[0] and 0 <= cx < self.element_map.shape[1]):
572
+ if (0 <= cy < self.element_map.shape[0]) and (
573
+ 0 <= cx < self.element_map.shape[1]
574
+ ):
599
575
  self.element_map[cy, cx] = DrawableElement.CHARGER
@@ -12,16 +12,16 @@ def get_blended_color(
12
12
  x: int,
13
13
  y: int,
14
14
  new_element: DrawableElement,
15
- new_color: Tuple[int, int, int, int]
15
+ new_color: Tuple[int, int, int, int],
16
16
  ) -> Tuple[int, int, int, int]:
17
17
  """
18
18
  Get a blended color for a pixel based on the current element map and the new element to draw.
19
-
19
+
20
20
  This function:
21
21
  1. Gets the current element at position (x,y) from the element map
22
22
  2. Gets the color for that element from the colors manager
23
23
  3. Blends the new color with the existing color based on alpha values
24
-
24
+
25
25
  Args:
26
26
  element_map_generator: The element map generator containing the current element map
27
27
  colors_manager: The colors manager to get colors for elements
@@ -29,20 +29,20 @@ def get_blended_color(
29
29
  y: Y coordinate in the element map
30
30
  new_element: The new element to draw at this position
31
31
  new_color: The RGBA color of the new element
32
-
32
+
33
33
  Returns:
34
34
  Blended RGBA color to use for drawing
35
35
  """
36
36
  # Get current element at this position
37
37
  current_element = element_map_generator.get_element_at_position(x, y)
38
-
38
+
39
39
  # If no current element or it's the same as the new element, just return the new color
40
40
  if current_element is None or current_element == new_element:
41
41
  return new_color
42
-
42
+
43
43
  # Get color for the current element
44
44
  current_color = None
45
-
45
+
46
46
  # Handle different element types
47
47
  if current_element == DrawableElement.FLOOR:
48
48
  # Floor is the background color
@@ -57,6 +57,6 @@ def get_blended_color(
57
57
  else:
58
58
  # Default for unknown elements
59
59
  current_color = (100, 100, 100, 255)
60
-
60
+
61
61
  # Blend the colors
62
62
  return colors_manager.blend_colors(current_color, new_color)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: valetudo-map-parser
3
- Version: 0.1.9b44
3
+ Version: 0.1.9b46
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
@@ -12,7 +12,6 @@ Classifier: Programming Language :: Python :: 3.12
12
12
  Classifier: Programming Language :: Python :: 3.13
13
13
  Requires-Dist: Pillow (>=10.3.0)
14
14
  Requires-Dist: numpy (>=1.26.4)
15
- Requires-Dist: scikit-image (>=0.22.0)
16
15
  Requires-Dist: scipy (>=1.12.0)
17
16
  Project-URL: Bug Tracker, https://github.com/sca075/Python-package-valetudo-map-parser/issues
18
17
  Project-URL: Changelog, https://github.com/sca075/Python-package-valetudo-map-parser/releases
@@ -0,0 +1,26 @@
1
+ valetudo_map_parser/__init__.py,sha256=INujZn4exaXUjqMN8nZkGJIziDWlW59t65fJ34HJX44,956
2
+ valetudo_map_parser/config/__init__.py,sha256=DQ9plV3ZF_K25Dp5ZQHPDoG-40dQoJNdNi-dfNeR3Zc,48
3
+ valetudo_map_parser/config/auto_crop.py,sha256=6OvRsWzXMXBaSEvgwpaaisNdozDKiDyTmPjknFxoUMc,12624
4
+ valetudo_map_parser/config/colors.py,sha256=90NkvzX7b2ckEWvTQSX6mvUDvS0IuiywMPX6__74iL0,30283
5
+ valetudo_map_parser/config/drawable.py,sha256=dZ6FZVch9GVIpT4k52I5iXWtSH9oD9KJArcK48p-OaA,23667
6
+ valetudo_map_parser/config/drawable_elements.py,sha256=Ulfgf8B4LuLCfx-FfmC7LrP8o9ll_Sncg9mR774_3KE,43140
7
+ valetudo_map_parser/config/enhanced_drawable.py,sha256=xNgFUNccstP245VgLFEA9gjB3-VvlSAJSjRgSZ3YFL0,16641
8
+ valetudo_map_parser/config/optimized_element_map.py,sha256=52BCnkvVv9bre52LeVIfT8nhnEIpc0TuWTv1xcNu0Rk,15744
9
+ valetudo_map_parser/config/rand25_parser.py,sha256=kIayyqVZBfQfAMkiArzqrrj9vqZB3pkgT0Y5ufrQmGA,16448
10
+ valetudo_map_parser/config/room_outline.py,sha256=D20D-yeyKnlmVbW9lI7bsPtQGn2XkcWow6YNOEPnWVg,4800
11
+ valetudo_map_parser/config/shared.py,sha256=WSl5rYSiTqE6YGAiwi9RILMZIQdFZRzVS8DwqzTZBbw,11309
12
+ valetudo_map_parser/config/types.py,sha256=uEJY-yYHHJWW3EZjg7hERSFrC2XuKzzRGT3C0z31Aw0,18359
13
+ valetudo_map_parser/config/utils.py,sha256=MP5_s9VFSdDERymujvDuGB8nYCXVuJcqg5tR5H9HCgY,33167
14
+ valetudo_map_parser/hypfer_draw.py,sha256=oL_RbX0LEcPvOlMrfBA38qpJkMqqVwR-oAEbZeHqLWM,19898
15
+ valetudo_map_parser/hypfer_handler.py,sha256=-nwGlfd-fqmNAHEeQFgCarr1t5v8gJTvb2ngDz_8PbM,27616
16
+ valetudo_map_parser/map_data.py,sha256=lSKD-CG-RENOcNUDnUIIpqh74DuGnLmrH46IF1_EjwQ,19117
17
+ valetudo_map_parser/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
18
+ valetudo_map_parser/rand25_handler.py,sha256=F9o1J6JZRV3CZTS4CG3AHNHZweKRM0nzd4HLmdPZe4w,23617
19
+ valetudo_map_parser/reimg_draw.py,sha256=1q8LkNTPHEA9Tsapc_JnVw51kpPYNhaBU-KmHkefCQY,12507
20
+ valetudo_map_parser/utils/__init__.py,sha256=r-GKKSPqBkMDd2K-vWe7kAix8OBrGN5HXC1RS2tbDwo,130
21
+ valetudo_map_parser/utils/color_utils.py,sha256=VogvNcITQHvtMUrVkzKcimvKtwSw0fjH72_RWtNoULA,2394
22
+ valetudo_map_parser-0.1.9b46.dist-info/LICENSE,sha256=Lh-qBbuRV0-jiCIBhfV7NgdwFxQFOXH3BKOzK865hRs,10480
23
+ valetudo_map_parser-0.1.9b46.dist-info/METADATA,sha256=7UIQEEVpOu_HR4FEIXeoVHSniZYc3FaNPtfZEJZTvMQ,3321
24
+ valetudo_map_parser-0.1.9b46.dist-info/NOTICE.txt,sha256=5lTOuWiU9aiEnJ2go8sc7lTJ7ntMBx0g0GFnNrswCY4,2533
25
+ valetudo_map_parser-0.1.9b46.dist-info/WHEEL,sha256=fGIA9gx4Qxk2KDKeNJCbOEwSrmLtjWCwzBz351GyrPQ,88
26
+ valetudo_map_parser-0.1.9b46.dist-info/RECORD,,