valetudo-map-parser 0.1.9b68__py3-none-any.whl → 0.1.9b69__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/config/async_utils.py +11 -4
- valetudo_map_parser/config/drawable.py +99 -13
- valetudo_map_parser/config/shared.py +6 -4
- valetudo_map_parser/config/utils.py +56 -16
- valetudo_map_parser/hypfer_draw.py +8 -19
- valetudo_map_parser/hypfer_handler.py +7 -1
- valetudo_map_parser/map_data.py +22 -11
- valetudo_map_parser/rand256_handler.py +0 -2
- {valetudo_map_parser-0.1.9b68.dist-info → valetudo_map_parser-0.1.9b69.dist-info}/METADATA +1 -1
- {valetudo_map_parser-0.1.9b68.dist-info → valetudo_map_parser-0.1.9b69.dist-info}/RECORD +13 -13
- {valetudo_map_parser-0.1.9b68.dist-info → valetudo_map_parser-0.1.9b69.dist-info}/LICENSE +0 -0
- {valetudo_map_parser-0.1.9b68.dist-info → valetudo_map_parser-0.1.9b69.dist-info}/NOTICE.txt +0 -0
- {valetudo_map_parser-0.1.9b68.dist-info → valetudo_map_parser-0.1.9b69.dist-info}/WHEEL +0 -0
@@ -23,7 +23,9 @@ class AsyncNumPy:
|
|
23
23
|
return await make_async(np.copy, array)
|
24
24
|
|
25
25
|
@staticmethod
|
26
|
-
async def async_full(
|
26
|
+
async def async_full(
|
27
|
+
shape: tuple, fill_value: Any, dtype: np.dtype = None
|
28
|
+
) -> np.ndarray:
|
27
29
|
"""Async array creation with fill value."""
|
28
30
|
return await make_async(np.full, shape, fill_value, dtype=dtype)
|
29
31
|
|
@@ -42,20 +44,25 @@ class AsyncPIL:
|
|
42
44
|
return await make_async(Image.fromarray, array, mode)
|
43
45
|
|
44
46
|
@staticmethod
|
45
|
-
async def async_resize(
|
47
|
+
async def async_resize(
|
48
|
+
image: Image.Image, size: tuple, resample: int = None
|
49
|
+
) -> Image.Image:
|
46
50
|
"""Async image resizing."""
|
47
51
|
if resample is None:
|
48
52
|
resample = Image.LANCZOS
|
49
53
|
return await make_async(image.resize, size, resample)
|
50
54
|
|
51
55
|
@staticmethod
|
52
|
-
async def async_save_to_bytes(
|
56
|
+
async def async_save_to_bytes(
|
57
|
+
image: Image.Image, format_type: str = "WEBP", **kwargs
|
58
|
+
) -> bytes:
|
53
59
|
"""Async image saving to bytes."""
|
60
|
+
|
54
61
|
def save_to_bytes():
|
55
62
|
buffer = io.BytesIO()
|
56
63
|
image.save(buffer, format=format_type, **kwargs)
|
57
64
|
return buffer.getvalue()
|
58
|
-
|
65
|
+
|
59
66
|
return await make_async(save_to_bytes)
|
60
67
|
|
61
68
|
|
@@ -311,6 +311,79 @@ class Drawable:
|
|
311
311
|
|
312
312
|
return layer
|
313
313
|
|
314
|
+
@staticmethod
|
315
|
+
def draw_lines_batch(
|
316
|
+
layer: NumpyArray,
|
317
|
+
line_segments: list,
|
318
|
+
color: Color,
|
319
|
+
width: int = 3,
|
320
|
+
) -> NumpyArray:
|
321
|
+
"""
|
322
|
+
Draw multiple line segments with batch processing for better performance.
|
323
|
+
|
324
|
+
Args:
|
325
|
+
layer: The numpy array to draw on
|
326
|
+
line_segments: List of tuples [(x1, y1, x2, y2), ...]
|
327
|
+
color: Color to draw with
|
328
|
+
width: Width of the lines
|
329
|
+
"""
|
330
|
+
if not line_segments:
|
331
|
+
return layer
|
332
|
+
|
333
|
+
# Pre-calculate blended color once for the entire batch
|
334
|
+
# Use the first line segment for color sampling
|
335
|
+
x1, y1, x2, y2 = line_segments[0]
|
336
|
+
blended_color = get_blended_color(x1, y1, x2, y2, layer, color)
|
337
|
+
|
338
|
+
# Fast path for fully opaque colors - skip individual blending
|
339
|
+
if color[3] == 255:
|
340
|
+
blended_color = color
|
341
|
+
|
342
|
+
# Process all line segments with the same blended color
|
343
|
+
for x1, y1, x2, y2 in line_segments:
|
344
|
+
# Ensure coordinates are integers
|
345
|
+
x1, y1, x2, y2 = int(x1), int(y1), int(x2), int(y2)
|
346
|
+
|
347
|
+
# Calculate line length
|
348
|
+
length = max(abs(x2 - x1), abs(y2 - y1))
|
349
|
+
if length == 0: # Handle case of a single point
|
350
|
+
# Draw a dot with the specified width
|
351
|
+
for i in range(-width // 2, (width + 1) // 2):
|
352
|
+
for j in range(-width // 2, (width + 1) // 2):
|
353
|
+
if (
|
354
|
+
0 <= x1 + i < layer.shape[1]
|
355
|
+
and 0 <= y1 + j < layer.shape[0]
|
356
|
+
):
|
357
|
+
layer[y1 + j, x1 + i] = blended_color
|
358
|
+
continue
|
359
|
+
|
360
|
+
# Create parametric points along the line
|
361
|
+
t = np.linspace(0, 1, length + 1) # Reduced from length * 2 to length + 1
|
362
|
+
x_coords = np.round(x1 * (1 - t) + x2 * t).astype(int)
|
363
|
+
y_coords = np.round(y1 * (1 - t) + y2 * t).astype(int)
|
364
|
+
|
365
|
+
# Draw the line with the specified width
|
366
|
+
if width == 1:
|
367
|
+
# Fast path for width=1
|
368
|
+
for x, y in zip(x_coords, y_coords):
|
369
|
+
if 0 <= x < layer.shape[1] and 0 <= y < layer.shape[0]:
|
370
|
+
layer[y, x] = blended_color
|
371
|
+
else:
|
372
|
+
# For thicker lines, draw a rectangle at each point
|
373
|
+
half_width = width // 2
|
374
|
+
for x, y in zip(x_coords, y_coords):
|
375
|
+
for i in range(-half_width, half_width + 1):
|
376
|
+
for j in range(-half_width, half_width + 1):
|
377
|
+
if (
|
378
|
+
i * i + j * j
|
379
|
+
<= half_width * half_width # Make it round
|
380
|
+
and 0 <= x + i < layer.shape[1]
|
381
|
+
and 0 <= y + j < layer.shape[0]
|
382
|
+
):
|
383
|
+
layer[y + j, x + i] = blended_color
|
384
|
+
|
385
|
+
return layer
|
386
|
+
|
314
387
|
@staticmethod
|
315
388
|
async def draw_virtual_walls(
|
316
389
|
layer: NumpyArray, virtual_walls, color: Color
|
@@ -329,14 +402,16 @@ class Drawable:
|
|
329
402
|
async def lines(arr: NumpyArray, coords, width: int, color: Color) -> NumpyArray:
|
330
403
|
"""
|
331
404
|
Join the coordinates creating a continuous line (path).
|
332
|
-
Optimized with
|
405
|
+
Optimized with batch processing for better performance.
|
333
406
|
"""
|
334
407
|
|
335
408
|
# Handle case where arr might be a coroutine (shouldn't happen but let's be safe)
|
336
409
|
if inspect.iscoroutine(arr):
|
337
410
|
arr = await arr
|
338
411
|
|
339
|
-
|
412
|
+
# Collect all line segments for batch processing
|
413
|
+
line_segments = []
|
414
|
+
for coord in coords:
|
340
415
|
x0, y0 = coord[0]
|
341
416
|
try:
|
342
417
|
x1, y1 = coord[1]
|
@@ -347,15 +422,16 @@ class Drawable:
|
|
347
422
|
if x0 == x1 and y0 == y1:
|
348
423
|
continue
|
349
424
|
|
350
|
-
|
351
|
-
blended_color = get_blended_color(x0, y0, x1, y1, arr, color)
|
425
|
+
line_segments.append((x0, y0, x1, y1))
|
352
426
|
|
353
|
-
|
354
|
-
|
427
|
+
# Process all line segments in batches
|
428
|
+
batch_size = 100 # Process 100 lines at a time
|
429
|
+
for i in range(0, len(line_segments), batch_size):
|
430
|
+
batch = line_segments[i : i + batch_size]
|
431
|
+
arr = Drawable.draw_lines_batch(arr, batch, color, width)
|
355
432
|
|
356
|
-
# Yield control
|
357
|
-
|
358
|
-
await asyncio.sleep(0)
|
433
|
+
# Yield control between batches to prevent blocking
|
434
|
+
await asyncio.sleep(0)
|
359
435
|
|
360
436
|
return arr
|
361
437
|
|
@@ -513,7 +589,11 @@ class Drawable:
|
|
513
589
|
# Create tasks for parallel zone processing
|
514
590
|
zone_tasks = []
|
515
591
|
for zone in coordinates:
|
516
|
-
zone_tasks.append(
|
592
|
+
zone_tasks.append(
|
593
|
+
Drawable._process_single_zone(
|
594
|
+
layers.copy(), zone, color, dot_radius, dot_spacing
|
595
|
+
)
|
596
|
+
)
|
517
597
|
|
518
598
|
# Execute all zone processing tasks in parallel
|
519
599
|
zone_results = await asyncio.gather(*zone_tasks, return_exceptions=True)
|
@@ -567,7 +647,9 @@ class Drawable:
|
|
567
647
|
y_indices, x_indices = np.ogrid[y_min:y_max, x_min:x_max]
|
568
648
|
|
569
649
|
# Create a circular mask
|
570
|
-
mask = (y_indices - y) ** 2 + (
|
650
|
+
mask = (y_indices - y) ** 2 + (
|
651
|
+
x_indices - x
|
652
|
+
) ** 2 <= dot_radius**2
|
571
653
|
|
572
654
|
# Apply the color to the masked region
|
573
655
|
layers[y_min:y_max, x_min:x_max][mask] = blended_color
|
@@ -575,7 +657,9 @@ class Drawable:
|
|
575
657
|
return layers
|
576
658
|
|
577
659
|
@staticmethod
|
578
|
-
async def _process_single_zone(
|
660
|
+
async def _process_single_zone(
|
661
|
+
layers: NumpyArray, zone, color: Color, dot_radius: int, dot_spacing: int
|
662
|
+
) -> NumpyArray:
|
579
663
|
"""Process a single zone for parallel execution."""
|
580
664
|
await asyncio.sleep(0) # Yield control
|
581
665
|
|
@@ -613,7 +697,9 @@ class Drawable:
|
|
613
697
|
|
614
698
|
if y_min < y_max and x_min < x_max:
|
615
699
|
y_indices, x_indices = np.ogrid[y_min:y_max, x_min:x_max]
|
616
|
-
mask = (y_indices - y) ** 2 + (
|
700
|
+
mask = (y_indices - y) ** 2 + (
|
701
|
+
x_indices - x
|
702
|
+
) ** 2 <= dot_radius**2
|
617
703
|
layers[y_min:y_max, x_min:x_max][mask] = blended_color
|
618
704
|
|
619
705
|
return layers
|
@@ -1,12 +1,13 @@
|
|
1
1
|
"""
|
2
2
|
Class Camera Shared.
|
3
3
|
Keep the data between the modules.
|
4
|
-
Version:
|
4
|
+
Version: v0.1.9
|
5
5
|
"""
|
6
6
|
|
7
7
|
import asyncio
|
8
8
|
import logging
|
9
9
|
from typing import List
|
10
|
+
from PIL import Image
|
10
11
|
|
11
12
|
from .types import (
|
12
13
|
ATTR_CALIBRATION_POINTS,
|
@@ -59,7 +60,10 @@ class CameraShared:
|
|
59
60
|
self.rand256_active_zone: list = [] # Active zone for rand256
|
60
61
|
self.is_rand: bool = False # MQTT rand data
|
61
62
|
self._new_mqtt_message = False # New MQTT message
|
62
|
-
|
63
|
+
# Initialize last_image with default gray image (250x150 minimum)
|
64
|
+
self.last_image = Image.new(
|
65
|
+
"RGBA", (250, 150), (128, 128, 128, 255)
|
66
|
+
) # Gray default image
|
63
67
|
self.new_image: PilPNG | None = None # New image received
|
64
68
|
self.binary_image: bytes | None = None # Current image in binary format
|
65
69
|
self.image_last_updated: float = 0.0 # Last image update time
|
@@ -115,8 +119,6 @@ class CameraShared:
|
|
115
119
|
self.skip_room_ids: List[str] = []
|
116
120
|
self.device_info = None # Store the device_info
|
117
121
|
|
118
|
-
|
119
|
-
|
120
122
|
def vacuum_bat_charged(self) -> bool:
|
121
123
|
"""Check if the vacuum is charging."""
|
122
124
|
return (self.vacuum_state == "docked") and (int(self.vacuum_battery) < 100)
|
@@ -11,7 +11,7 @@ import numpy as np
|
|
11
11
|
from PIL import Image, ImageOps
|
12
12
|
|
13
13
|
from .drawable import Drawable
|
14
|
-
from .drawable_elements import
|
14
|
+
from .drawable_elements import DrawingConfig
|
15
15
|
from .enhanced_drawable import EnhancedDrawable
|
16
16
|
from .types import (
|
17
17
|
LOGGER,
|
@@ -80,10 +80,11 @@ class BaseHandler:
|
|
80
80
|
"""Return the robot position."""
|
81
81
|
return self.robot_pos
|
82
82
|
|
83
|
-
async def
|
83
|
+
async def async_get_image(
|
84
84
|
self,
|
85
85
|
m_json: dict | None,
|
86
86
|
destinations: list | None = None,
|
87
|
+
bytes_format: bool = False,
|
87
88
|
) -> PilPNG | None:
|
88
89
|
"""
|
89
90
|
Unified async function to get PIL image from JSON data for both Hypfer and Rand256 handlers.
|
@@ -96,50 +97,89 @@ class BaseHandler:
|
|
96
97
|
|
97
98
|
@param m_json: The JSON data to use to draw the image
|
98
99
|
@param destinations: MQTT destinations for labels (used by Rand256)
|
100
|
+
@param bytes_format: If True, also convert to PNG bytes and store in shared.binary_image
|
99
101
|
@return: PIL Image or None
|
100
102
|
"""
|
101
103
|
try:
|
102
104
|
# Backup current image to last_image before processing new one
|
103
|
-
if hasattr(self.shared,
|
105
|
+
if hasattr(self.shared, "new_image") and self.shared.new_image is not None:
|
104
106
|
self.shared.last_image = self.shared.new_image
|
105
107
|
|
106
108
|
# Call the appropriate handler method based on handler type
|
107
|
-
if hasattr(self,
|
109
|
+
if hasattr(self, "get_image_from_rrm"):
|
108
110
|
# This is a Rand256 handler
|
109
111
|
new_image = await self.get_image_from_rrm(
|
110
112
|
m_json=m_json,
|
111
113
|
destinations=destinations,
|
112
|
-
return_webp=False # Always return PIL Image
|
114
|
+
return_webp=False, # Always return PIL Image
|
113
115
|
)
|
114
|
-
elif hasattr(self,
|
116
|
+
elif hasattr(self, "async_get_image_from_json"):
|
115
117
|
# This is a Hypfer handler
|
116
118
|
new_image = await self.async_get_image_from_json(
|
117
119
|
m_json=m_json,
|
118
|
-
return_webp=False # Always return PIL Image
|
120
|
+
return_webp=False, # Always return PIL Image
|
119
121
|
)
|
120
122
|
else:
|
121
|
-
LOGGER.warning(
|
122
|
-
|
123
|
+
LOGGER.warning(
|
124
|
+
"%s: Handler type not recognized for async_get_image",
|
125
|
+
self.file_name,
|
126
|
+
)
|
127
|
+
return (
|
128
|
+
self.shared.last_image
|
129
|
+
if hasattr(self.shared, "last_image")
|
130
|
+
else None
|
131
|
+
)
|
123
132
|
|
124
133
|
# Store the new image in shared data
|
125
134
|
if new_image is not None:
|
126
135
|
self.shared.new_image = new_image
|
136
|
+
|
137
|
+
# Convert to binary (PNG bytes) if requested
|
138
|
+
if bytes_format:
|
139
|
+
try:
|
140
|
+
png_buffer = io.BytesIO()
|
141
|
+
new_image.save(png_buffer, format="PNG")
|
142
|
+
self.shared.binary_image = png_buffer.getvalue()
|
143
|
+
png_buffer.close()
|
144
|
+
LOGGER.debug(
|
145
|
+
"%s: Binary image conversion completed", self.file_name
|
146
|
+
)
|
147
|
+
except Exception as e:
|
148
|
+
LOGGER.warning(
|
149
|
+
"%s: Failed to convert image to binary: %s",
|
150
|
+
self.file_name,
|
151
|
+
str(e),
|
152
|
+
)
|
153
|
+
self.shared.binary_image = None
|
154
|
+
else:
|
155
|
+
self.shared.binary_image = None
|
156
|
+
|
127
157
|
# Update the timestamp with current datetime
|
128
158
|
self.shared.image_last_updated = datetime.datetime.now().timestamp()
|
129
|
-
LOGGER.debug(
|
159
|
+
LOGGER.debug(
|
160
|
+
"%s: Image processed and stored in shared data", self.file_name
|
161
|
+
)
|
162
|
+
return new_image
|
130
163
|
else:
|
131
|
-
LOGGER.warning(
|
132
|
-
|
133
|
-
|
164
|
+
LOGGER.warning(
|
165
|
+
"%s: Failed to generate image from JSON data", self.file_name
|
166
|
+
)
|
167
|
+
return (
|
168
|
+
self.shared.last_image
|
169
|
+
if hasattr(self.shared, "last_image")
|
170
|
+
else None
|
171
|
+
)
|
134
172
|
|
135
173
|
except Exception as e:
|
136
174
|
LOGGER.error(
|
137
|
-
"%s: Error in
|
175
|
+
"%s: Error in async_get_image: %s",
|
138
176
|
self.file_name,
|
139
177
|
str(e),
|
140
|
-
exc_info=True
|
178
|
+
exc_info=True,
|
179
|
+
)
|
180
|
+
return (
|
181
|
+
self.shared.last_image if hasattr(self.shared, "last_image") else None
|
141
182
|
)
|
142
|
-
return None
|
143
183
|
|
144
184
|
def get_charger_position(self) -> ChargerPosition | None:
|
145
185
|
"""Return the charger position."""
|
@@ -337,39 +337,30 @@ class ImageDraw:
|
|
337
337
|
_LOGGER.info("%s: Got zones.", self.file_name)
|
338
338
|
|
339
339
|
if zone_clean:
|
340
|
-
#
|
341
|
-
|
340
|
+
# Process zones sequentially to avoid memory-intensive array copies
|
341
|
+
# This is more memory-efficient than parallel processing with copies
|
342
342
|
|
343
343
|
# Active zones
|
344
344
|
zones_active = zone_clean.get("active_zone")
|
345
345
|
if zones_active:
|
346
|
-
|
347
|
-
|
346
|
+
np_array = await self.img_h.draw.zones(
|
347
|
+
np_array, zones_active, color_zone_clean
|
348
348
|
)
|
349
349
|
|
350
350
|
# No-go zones
|
351
351
|
no_go_zones = zone_clean.get("no_go_area")
|
352
352
|
if no_go_zones:
|
353
|
-
|
354
|
-
|
353
|
+
np_array = await self.img_h.draw.zones(
|
354
|
+
np_array, no_go_zones, color_no_go
|
355
355
|
)
|
356
356
|
|
357
357
|
# No-mop zones
|
358
358
|
no_mop_zones = zone_clean.get("no_mop_area")
|
359
359
|
if no_mop_zones:
|
360
|
-
|
361
|
-
|
360
|
+
np_array = await self.img_h.draw.zones(
|
361
|
+
np_array, no_mop_zones, color_no_go
|
362
362
|
)
|
363
363
|
|
364
|
-
# Execute all zone drawing tasks in parallel
|
365
|
-
if zone_tasks:
|
366
|
-
zone_results = await asyncio.gather(*zone_tasks)
|
367
|
-
# Merge results back into the main array
|
368
|
-
for result in zone_results:
|
369
|
-
# Simple overlay - in practice you might want more sophisticated blending
|
370
|
-
mask = result != np_array
|
371
|
-
np_array[mask] = result[mask]
|
372
|
-
|
373
364
|
return np_array
|
374
365
|
|
375
366
|
async def async_draw_virtual_walls(
|
@@ -439,7 +430,6 @@ class ImageDraw:
|
|
439
430
|
def _check_active_zone_and_set_zooming(self) -> None:
|
440
431
|
"""Helper function to check active zones and set zooming state."""
|
441
432
|
if self.img_h.active_zones and self.img_h.robot_in_room:
|
442
|
-
|
443
433
|
segment_id = str(self.img_h.robot_in_room["id"])
|
444
434
|
room_store = RoomStore(self.file_name)
|
445
435
|
room_keys = list(room_store.get_rooms().keys())
|
@@ -615,7 +605,6 @@ class ImageDraw:
|
|
615
605
|
|
616
606
|
# Handle active zones - Map segment ID to active_zones position
|
617
607
|
if self.img_h.active_zones:
|
618
|
-
|
619
608
|
segment_id = str(self.img_h.robot_in_room["id"])
|
620
609
|
room_store = RoomStore(self.file_name)
|
621
610
|
room_keys = list(room_store.get_rooms().keys())
|
@@ -301,11 +301,17 @@ class HypferMapImageHandler(BaseHandler, AutoCrop):
|
|
301
301
|
|
302
302
|
# Prepare path data extraction
|
303
303
|
path_enabled = self.drawing_config.is_enabled(DrawableElement.PATH)
|
304
|
-
LOGGER.info(
|
304
|
+
LOGGER.info(
|
305
|
+
"%s: PATH element enabled: %s", self.file_name, path_enabled
|
306
|
+
)
|
305
307
|
if path_enabled:
|
306
308
|
LOGGER.info("%s: Drawing path", self.file_name)
|
307
309
|
data_tasks.append(self._prepare_path_data(m_json))
|
308
310
|
|
311
|
+
# Await all data preparation tasks if any were created
|
312
|
+
if data_tasks:
|
313
|
+
await asyncio.gather(*data_tasks)
|
314
|
+
|
309
315
|
# Process drawing operations sequentially (since they modify the same array)
|
310
316
|
# Draw zones if enabled
|
311
317
|
if self.drawing_config.is_enabled(DrawableElement.RESTRICTED_AREA):
|
valetudo_map_parser/map_data.py
CHANGED
@@ -19,14 +19,14 @@ class ImageData:
|
|
19
19
|
@staticmethod
|
20
20
|
def sublist(lst, n):
|
21
21
|
"""Split a list into n chunks of specified size."""
|
22
|
-
return [lst[i: i + n] for i in range(0, len(lst), n)]
|
22
|
+
return [lst[i : i + n] for i in range(0, len(lst), n)]
|
23
23
|
|
24
24
|
@staticmethod
|
25
25
|
def sublist_join(lst, n):
|
26
26
|
"""Join the lists in a unique list of n elements."""
|
27
27
|
arr = np.array(lst)
|
28
28
|
num_windows = len(lst) - n + 1
|
29
|
-
result = [arr[i: i + n].tolist() for i in range(num_windows)]
|
29
|
+
result = [arr[i : i + n].tolist() for i in range(num_windows)]
|
30
30
|
return result
|
31
31
|
|
32
32
|
@staticmethod
|
@@ -39,15 +39,19 @@ class ImageData:
|
|
39
39
|
points = obstacle.get("points", [])
|
40
40
|
image_id = obstacle.get("metaData", {}).get("id")
|
41
41
|
if label and points:
|
42
|
-
obstacle_positions.append(
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
42
|
+
obstacle_positions.append(
|
43
|
+
{
|
44
|
+
"label": label,
|
45
|
+
"points": {"x": points[0], "y": points[1]},
|
46
|
+
"id": image_id,
|
47
|
+
}
|
48
|
+
)
|
47
49
|
return obstacle_positions
|
48
50
|
|
49
51
|
@staticmethod
|
50
|
-
def find_layers(
|
52
|
+
def find_layers(
|
53
|
+
json_obj: JsonType, layer_dict: dict, active_list: list
|
54
|
+
) -> tuple[dict, list]:
|
51
55
|
"""Find the layers in the json object."""
|
52
56
|
layer_dict = {} if layer_dict is None else layer_dict
|
53
57
|
active_list = [] if active_list is None else active_list
|
@@ -56,7 +60,9 @@ class ImageData:
|
|
56
60
|
layer_type = json_obj.get("type")
|
57
61
|
active_type = json_obj.get("metaData")
|
58
62
|
if layer_type:
|
59
|
-
layer_dict.setdefault(layer_type, []).append(
|
63
|
+
layer_dict.setdefault(layer_type, []).append(
|
64
|
+
json_obj.get("compressedPixels", [])
|
65
|
+
)
|
60
66
|
if layer_type == "segment":
|
61
67
|
active_list.append(int(active_type.get("active", 0)))
|
62
68
|
for value in json_obj.values():
|
@@ -121,7 +127,10 @@ class ImageData:
|
|
121
127
|
|
122
128
|
def _recursive(obj):
|
123
129
|
if isinstance(obj, dict):
|
124
|
-
if
|
130
|
+
if (
|
131
|
+
obj.get("__class") == "LineMapEntity"
|
132
|
+
and obj.get("type") == "virtual_wall"
|
133
|
+
):
|
125
134
|
walls.append(obj["points"])
|
126
135
|
for value in obj.values():
|
127
136
|
_recursive(value)
|
@@ -133,7 +142,9 @@ class ImageData:
|
|
133
142
|
return walls
|
134
143
|
|
135
144
|
@staticmethod
|
136
|
-
async def async_get_rooms_coordinates(
|
145
|
+
async def async_get_rooms_coordinates(
|
146
|
+
pixels: list, pixel_size: int = 5, rand: bool = False
|
147
|
+
) -> tuple:
|
137
148
|
"""Extract the room coordinates from the vacuum pixels data."""
|
138
149
|
df = pd.DataFrame(pixels, columns=["x", "y", "length"])
|
139
150
|
if rand:
|
@@ -1,27 +1,27 @@
|
|
1
1
|
valetudo_map_parser/__init__.py,sha256=XO_eJwFDyU7hXJ4tAa2zY-n-SM2_kmIGMWDKY3GcauY,1163
|
2
2
|
valetudo_map_parser/config/__init__.py,sha256=DQ9plV3ZF_K25Dp5ZQHPDoG-40dQoJNdNi-dfNeR3Zc,48
|
3
|
-
valetudo_map_parser/config/async_utils.py,sha256=
|
3
|
+
valetudo_map_parser/config/async_utils.py,sha256=e1j9uTtg4dhPVWvB2_XgqaH4aeSjRAPz-puRMbGoOs8,3204
|
4
4
|
valetudo_map_parser/config/auto_crop.py,sha256=Aes7vfv4z8ihYvGaH5Nryj6Y9mHDerZLIeyvePjf9aQ,19259
|
5
5
|
valetudo_map_parser/config/color_utils.py,sha256=nXD6WeNmdFdoMxPDW-JFpjnxJSaZR1jX-ouNfrx6zvE,4502
|
6
6
|
valetudo_map_parser/config/colors.py,sha256=DG-oPQoN5gsnwDbEsuFr8a0hRCxmbFHObWa4_5pr-70,29910
|
7
|
-
valetudo_map_parser/config/drawable.py,sha256=
|
7
|
+
valetudo_map_parser/config/drawable.py,sha256=nAOoPQmNz6LJ4-WN7WF5iHTYEAzT43YVnTpiqzjNva0,41180
|
8
8
|
valetudo_map_parser/config/drawable_elements.py,sha256=o-5oiXmfqPwNQLzKIhkEcZD_A47rIU9E0CqKgWipxgc,11516
|
9
9
|
valetudo_map_parser/config/enhanced_drawable.py,sha256=QlGxlUMVgECUXPtFwIslyjubWxQuhIixsRymWV3lEvk,12586
|
10
10
|
valetudo_map_parser/config/optimized_element_map.py,sha256=52BCnkvVv9bre52LeVIfT8nhnEIpc0TuWTv1xcNu0Rk,15744
|
11
11
|
valetudo_map_parser/config/rand256_parser.py,sha256=LU3y7XvRRQxVen9iwom0dOaDnJJvhZdg97NqOYRZFas,16279
|
12
|
-
valetudo_map_parser/config/shared.py,sha256=
|
12
|
+
valetudo_map_parser/config/shared.py,sha256=98CgGDY0tbc5BSg2TIHbGcDFZZ2acgIYnoPjAwENmBU,12885
|
13
13
|
valetudo_map_parser/config/types.py,sha256=saL7pULKAdTRQ_ShR2arT8IV472e9MBC_SohTthlGp8,17567
|
14
|
-
valetudo_map_parser/config/utils.py,sha256=
|
15
|
-
valetudo_map_parser/hypfer_draw.py,sha256=
|
16
|
-
valetudo_map_parser/hypfer_handler.py,sha256=
|
14
|
+
valetudo_map_parser/config/utils.py,sha256=lRLvQbqCQ44knnIJr-UAlkEOO1d14mFCmHKDor989VE,35468
|
15
|
+
valetudo_map_parser/hypfer_draw.py,sha256=vlrfBSDpHNnjh0dRA1BE5_4MsKMMWS_O1QErrd-VRKE,28995
|
16
|
+
valetudo_map_parser/hypfer_handler.py,sha256=U452BEi3kmU0O27aNN7BY42J_zrpjhDuebpqxwGxe50,23366
|
17
17
|
valetudo_map_parser/hypfer_rooms_handler.py,sha256=NkpOA6Gdq-2D3lLAxvtNuuWMvPXHxeMY2TO5RZLSHlU,22652
|
18
|
-
valetudo_map_parser/map_data.py,sha256=
|
18
|
+
valetudo_map_parser/map_data.py,sha256=Flq5t9QQQiD5ylObIniHhPP1VB7VhNoMcMeJrOey3Go,17433
|
19
19
|
valetudo_map_parser/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
20
|
-
valetudo_map_parser/rand256_handler.py,sha256=
|
20
|
+
valetudo_map_parser/rand256_handler.py,sha256=daaSQ5ktMUYMnYxJkjS75UdBchpXVZ58HIomwHBFivs,27651
|
21
21
|
valetudo_map_parser/reimg_draw.py,sha256=1q8LkNTPHEA9Tsapc_JnVw51kpPYNhaBU-KmHkefCQY,12507
|
22
22
|
valetudo_map_parser/rooms_handler.py,sha256=ovqQtAjauAqwUNPR0aX27P2zhheQmqfaFhDE3_AwYWk,17821
|
23
|
-
valetudo_map_parser-0.1.
|
24
|
-
valetudo_map_parser-0.1.
|
25
|
-
valetudo_map_parser-0.1.
|
26
|
-
valetudo_map_parser-0.1.
|
27
|
-
valetudo_map_parser-0.1.
|
23
|
+
valetudo_map_parser-0.1.9b69.dist-info/LICENSE,sha256=Lh-qBbuRV0-jiCIBhfV7NgdwFxQFOXH3BKOzK865hRs,10480
|
24
|
+
valetudo_map_parser-0.1.9b69.dist-info/METADATA,sha256=Ex2vOq_SjHDzJqk5dNNL78hXqOnkhz9Y7anOUC-XsmE,3353
|
25
|
+
valetudo_map_parser-0.1.9b69.dist-info/NOTICE.txt,sha256=5lTOuWiU9aiEnJ2go8sc7lTJ7ntMBx0g0GFnNrswCY4,2533
|
26
|
+
valetudo_map_parser-0.1.9b69.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
|
27
|
+
valetudo_map_parser-0.1.9b69.dist-info/RECORD,,
|
File without changes
|
{valetudo_map_parser-0.1.9b68.dist-info → valetudo_map_parser-0.1.9b69.dist-info}/NOTICE.txt
RENAMED
File without changes
|
File without changes
|