valetudo-map-parser 0.1.9b50__py3-none-any.whl → 0.1.9b51__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 +2 -0
- valetudo_map_parser/config/color_utils.py +69 -13
- valetudo_map_parser/config/colors.py +55 -70
- valetudo_map_parser/config/drawable_elements.py +0 -676
- valetudo_map_parser/config/enhanced_drawable.py +75 -193
- valetudo_map_parser/config/shared.py +0 -1
- valetudo_map_parser/config/types.py +19 -33
- valetudo_map_parser/config/utils.py +0 -67
- valetudo_map_parser/hypfer_draw.py +222 -57
- valetudo_map_parser/hypfer_handler.py +48 -155
- valetudo_map_parser/hypfer_rooms_handler.py +406 -0
- valetudo_map_parser/map_data.py +0 -30
- valetudo_map_parser/rand25_handler.py +2 -84
- {valetudo_map_parser-0.1.9b50.dist-info → valetudo_map_parser-0.1.9b51.dist-info}/METADATA +1 -1
- valetudo_map_parser-0.1.9b51.dist-info/RECORD +26 -0
- valetudo_map_parser-0.1.9b50.dist-info/RECORD +0 -25
- {valetudo_map_parser-0.1.9b50.dist-info → valetudo_map_parser-0.1.9b51.dist-info}/LICENSE +0 -0
- {valetudo_map_parser-0.1.9b50.dist-info → valetudo_map_parser-0.1.9b51.dist-info}/NOTICE.txt +0 -0
- {valetudo_map_parser-0.1.9b50.dist-info → valetudo_map_parser-0.1.9b51.dist-info}/WHEEL +0 -0
@@ -23,6 +23,51 @@ class ImageDraw:
|
|
23
23
|
self.img_h = image_handler
|
24
24
|
self.file_name = self.img_h.shared.file_name
|
25
25
|
|
26
|
+
@staticmethod
|
27
|
+
def point_in_polygon(x: int, y: int, polygon: list) -> bool:
|
28
|
+
"""
|
29
|
+
Check if a point is inside a polygon using ray casting algorithm.
|
30
|
+
Enhanced version with better handling of edge cases.
|
31
|
+
|
32
|
+
Args:
|
33
|
+
x: X coordinate of the point
|
34
|
+
y: Y coordinate of the point
|
35
|
+
polygon: List of (x, y) tuples forming the polygon
|
36
|
+
|
37
|
+
Returns:
|
38
|
+
True if the point is inside the polygon, False otherwise
|
39
|
+
"""
|
40
|
+
# Ensure we have a valid polygon with at least 3 points
|
41
|
+
if len(polygon) < 3:
|
42
|
+
return False
|
43
|
+
|
44
|
+
# Make sure the polygon is closed (last point equals first point)
|
45
|
+
if polygon[0] != polygon[-1]:
|
46
|
+
polygon = polygon + [polygon[0]]
|
47
|
+
|
48
|
+
# Use winding number algorithm for better accuracy
|
49
|
+
wn = 0 # Winding number counter
|
50
|
+
|
51
|
+
# Loop through all edges of the polygon
|
52
|
+
for i in range(len(polygon) - 1): # Last vertex is first vertex
|
53
|
+
p1x, p1y = polygon[i]
|
54
|
+
p2x, p2y = polygon[i + 1]
|
55
|
+
|
56
|
+
# Test if a point is left/right/on the edge defined by two vertices
|
57
|
+
if p1y <= y: # Start y <= P.y
|
58
|
+
if p2y > y: # End y > P.y (upward crossing)
|
59
|
+
# Point left of edge
|
60
|
+
if ((p2x - p1x) * (y - p1y) - (x - p1x) * (p2y - p1y)) > 0:
|
61
|
+
wn += 1 # Valid up intersect
|
62
|
+
else: # Start y > P.y
|
63
|
+
if p2y <= y: # End y <= P.y (downward crossing)
|
64
|
+
# Point right of edge
|
65
|
+
if ((p2x - p1x) * (y - p1y) - (x - p1x) * (p2y - p1y)) < 0:
|
66
|
+
wn -= 1 # Valid down intersect
|
67
|
+
|
68
|
+
# If winding number is not 0, the point is inside the polygon
|
69
|
+
return wn != 0
|
70
|
+
|
26
71
|
async def draw_go_to_flag(
|
27
72
|
self, np_array: NumpyArray, entity_dict: dict, color_go_to: Color
|
28
73
|
) -> NumpyArray:
|
@@ -414,75 +459,196 @@ class ImageDraw:
|
|
414
459
|
_LOGGER.info("%s: Got the points in the json.", self.file_name)
|
415
460
|
return entity_dict
|
416
461
|
|
462
|
+
@staticmethod
|
463
|
+
def point_in_polygon(x: int, y: int, polygon: list) -> bool:
|
464
|
+
"""
|
465
|
+
Check if a point is inside a polygon using ray casting algorithm.
|
466
|
+
|
467
|
+
Args:
|
468
|
+
x: X coordinate of the point
|
469
|
+
y: Y coordinate of the point
|
470
|
+
polygon: List of (x, y) tuples forming the polygon
|
471
|
+
|
472
|
+
Returns:
|
473
|
+
True if the point is inside the polygon, False otherwise
|
474
|
+
"""
|
475
|
+
n = len(polygon)
|
476
|
+
inside = False
|
477
|
+
|
478
|
+
p1x, p1y = polygon[0]
|
479
|
+
xinters = None # Initialize with default value
|
480
|
+
for i in range(1, n + 1):
|
481
|
+
p2x, p2y = polygon[i % n]
|
482
|
+
if y > min(p1y, p2y):
|
483
|
+
if y <= max(p1y, p2y):
|
484
|
+
if x <= max(p1x, p2x):
|
485
|
+
if p1y != p2y:
|
486
|
+
xinters = (y - p1y) * (p2x - p1x) / (p2y - p1y) + p1x
|
487
|
+
if p1x == p2x or (xinters is not None and x <= xinters):
|
488
|
+
inside = not inside
|
489
|
+
p1x, p1y = p2x, p2y
|
490
|
+
|
491
|
+
return inside
|
492
|
+
|
417
493
|
async def async_get_robot_in_room(
|
418
494
|
self, robot_y: int = 0, robot_x: int = 0, angle: float = 0.0
|
419
495
|
) -> RobotPosition:
|
420
496
|
"""Get the robot position and return in what room is."""
|
497
|
+
# First check if we already have a cached room and if the robot is still in it
|
421
498
|
if self.img_h.robot_in_room:
|
422
|
-
#
|
423
|
-
if
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
|
499
|
+
# If we have outline data, use point_in_polygon for accurate detection
|
500
|
+
if "outline" in self.img_h.robot_in_room:
|
501
|
+
outline = self.img_h.robot_in_room["outline"]
|
502
|
+
if self.point_in_polygon(int(robot_x), int(robot_y), outline):
|
503
|
+
temp = {
|
504
|
+
"x": robot_x,
|
505
|
+
"y": robot_y,
|
506
|
+
"angle": angle,
|
507
|
+
"in_room": self.img_h.robot_in_room["room"],
|
508
|
+
}
|
509
|
+
# Handle active zones
|
510
|
+
if self.img_h.active_zones and (
|
511
|
+
self.img_h.robot_in_room["id"]
|
512
|
+
in range(len(self.img_h.active_zones))
|
513
|
+
):
|
514
|
+
self.img_h.zooming = bool(
|
515
|
+
self.img_h.active_zones[self.img_h.robot_in_room["id"]]
|
516
|
+
)
|
517
|
+
else:
|
518
|
+
self.img_h.zooming = False
|
519
|
+
return temp
|
520
|
+
# Fallback to bounding box check if no outline data
|
521
|
+
elif all(
|
522
|
+
k in self.img_h.robot_in_room for k in ["left", "right", "up", "down"]
|
429
523
|
):
|
430
|
-
|
431
|
-
"
|
432
|
-
"
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
self.img_h.
|
442
|
-
|
443
|
-
|
444
|
-
self.img_h.
|
445
|
-
|
446
|
-
|
524
|
+
if (
|
525
|
+
(self.img_h.robot_in_room["right"] >= int(robot_x))
|
526
|
+
and (self.img_h.robot_in_room["left"] <= int(robot_x))
|
527
|
+
) and (
|
528
|
+
(self.img_h.robot_in_room["down"] >= int(robot_y))
|
529
|
+
and (self.img_h.robot_in_room["up"] <= int(robot_y))
|
530
|
+
):
|
531
|
+
temp = {
|
532
|
+
"x": robot_x,
|
533
|
+
"y": robot_y,
|
534
|
+
"angle": angle,
|
535
|
+
"in_room": self.img_h.robot_in_room["room"],
|
536
|
+
}
|
537
|
+
# Handle active zones
|
538
|
+
if self.img_h.active_zones and (
|
539
|
+
self.img_h.robot_in_room["id"]
|
540
|
+
in range(len(self.img_h.active_zones))
|
541
|
+
):
|
542
|
+
self.img_h.zooming = bool(
|
543
|
+
self.img_h.active_zones[self.img_h.robot_in_room["id"]]
|
544
|
+
)
|
545
|
+
else:
|
546
|
+
self.img_h.zooming = False
|
547
|
+
return temp
|
548
|
+
|
549
|
+
# If we don't have a cached room or the robot is not in it, search all rooms
|
447
550
|
last_room = None
|
448
551
|
room_count = 0
|
449
552
|
if self.img_h.robot_in_room:
|
450
553
|
last_room = self.img_h.robot_in_room
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
"
|
458
|
-
|
459
|
-
|
554
|
+
|
555
|
+
# Check if the robot is far outside the normal map boundaries
|
556
|
+
# This helps prevent false positives for points very far from any room
|
557
|
+
map_boundary = 20000 # Typical map size is around 5000-10000 units
|
558
|
+
if abs(robot_x) > map_boundary or abs(robot_y) > map_boundary:
|
559
|
+
_LOGGER.debug(
|
560
|
+
"%s robot position (%s, %s) is far outside map boundaries.",
|
561
|
+
self.file_name,
|
562
|
+
robot_x,
|
563
|
+
robot_y,
|
564
|
+
)
|
565
|
+
self.img_h.robot_in_room = last_room
|
566
|
+
self.img_h.zooming = False
|
567
|
+
temp = {
|
568
|
+
"x": robot_x,
|
569
|
+
"y": robot_y,
|
570
|
+
"angle": angle,
|
571
|
+
"in_room": last_room["room"] if last_room else None,
|
460
572
|
}
|
461
|
-
|
462
|
-
|
463
|
-
|
464
|
-
|
465
|
-
|
466
|
-
|
467
|
-
|
468
|
-
|
469
|
-
|
470
|
-
|
471
|
-
|
472
|
-
|
473
|
-
|
474
|
-
|
573
|
+
return temp
|
574
|
+
|
575
|
+
# Search through all rooms to find which one contains the robot
|
576
|
+
if self.img_h.rooms_pos is None:
|
577
|
+
_LOGGER.debug(
|
578
|
+
"%s: No rooms data available for robot position detection.",
|
579
|
+
self.file_name,
|
580
|
+
)
|
581
|
+
self.img_h.robot_in_room = last_room
|
582
|
+
self.img_h.zooming = False
|
583
|
+
temp = {
|
584
|
+
"x": robot_x,
|
585
|
+
"y": robot_y,
|
586
|
+
"angle": angle,
|
587
|
+
"in_room": last_room["room"] if last_room else None,
|
588
|
+
}
|
589
|
+
return temp
|
590
|
+
|
591
|
+
for room in self.img_h.rooms_pos:
|
592
|
+
# Check if the room has an outline (polygon points)
|
593
|
+
if "outline" in room:
|
594
|
+
outline = room["outline"]
|
595
|
+
# Use point_in_polygon for accurate detection with complex shapes
|
596
|
+
if self.point_in_polygon(int(robot_x), int(robot_y), outline):
|
597
|
+
# Robot is in this room
|
598
|
+
self.img_h.robot_in_room = {
|
599
|
+
"id": room_count,
|
600
|
+
"room": str(room["name"]),
|
601
|
+
"outline": outline,
|
602
|
+
}
|
603
|
+
temp = {
|
604
|
+
"x": robot_x,
|
605
|
+
"y": robot_y,
|
606
|
+
"angle": angle,
|
607
|
+
"in_room": self.img_h.robot_in_room["room"],
|
608
|
+
}
|
609
|
+
_LOGGER.debug(
|
610
|
+
"%s is in %s room (polygon detection).",
|
611
|
+
self.file_name,
|
612
|
+
self.img_h.robot_in_room["room"],
|
613
|
+
)
|
614
|
+
return temp
|
615
|
+
# Fallback to bounding box if no outline is available
|
616
|
+
elif "corners" in room:
|
617
|
+
corners = room["corners"]
|
618
|
+
# Create a bounding box from the corners
|
619
|
+
self.img_h.robot_in_room = {
|
620
|
+
"id": room_count,
|
621
|
+
"left": int(corners[0][0]),
|
622
|
+
"right": int(corners[2][0]),
|
623
|
+
"up": int(corners[0][1]),
|
624
|
+
"down": int(corners[2][1]),
|
625
|
+
"room": str(room["name"]),
|
475
626
|
}
|
476
|
-
|
477
|
-
|
478
|
-
self.
|
479
|
-
self.img_h.robot_in_room["
|
480
|
-
)
|
481
|
-
|
482
|
-
|
483
|
-
|
627
|
+
# Check if the robot is inside the bounding box
|
628
|
+
if (
|
629
|
+
(self.img_h.robot_in_room["right"] >= int(robot_x))
|
630
|
+
and (self.img_h.robot_in_room["left"] <= int(robot_x))
|
631
|
+
) and (
|
632
|
+
(self.img_h.robot_in_room["down"] >= int(robot_y))
|
633
|
+
and (self.img_h.robot_in_room["up"] <= int(robot_y))
|
634
|
+
):
|
635
|
+
temp = {
|
636
|
+
"x": robot_x,
|
637
|
+
"y": robot_y,
|
638
|
+
"angle": angle,
|
639
|
+
"in_room": self.img_h.robot_in_room["room"],
|
640
|
+
}
|
641
|
+
_LOGGER.debug(
|
642
|
+
"%s is in %s room (bounding box detection).",
|
643
|
+
self.file_name,
|
644
|
+
self.img_h.robot_in_room["room"],
|
645
|
+
)
|
646
|
+
return temp
|
647
|
+
room_count += 1
|
648
|
+
|
649
|
+
# Robot not found in any room
|
484
650
|
_LOGGER.debug(
|
485
|
-
"%s not located within
|
651
|
+
"%s not located within any room coordinates.",
|
486
652
|
self.file_name,
|
487
653
|
)
|
488
654
|
self.img_h.robot_in_room = last_room
|
@@ -493,7 +659,6 @@ class ImageDraw:
|
|
493
659
|
"angle": angle,
|
494
660
|
"in_room": last_room["room"] if last_room else None,
|
495
661
|
}
|
496
|
-
# If the robot is not inside any room, return a default value
|
497
662
|
return temp
|
498
663
|
|
499
664
|
async def async_get_robot_position(self, entity_dict: dict) -> tuple | None:
|
@@ -9,12 +9,10 @@ from __future__ import annotations
|
|
9
9
|
|
10
10
|
import json
|
11
11
|
|
12
|
-
import numpy as np
|
13
12
|
from PIL import Image
|
14
13
|
|
15
14
|
from .config.auto_crop import AutoCrop
|
16
15
|
from .config.drawable_elements import DrawableElement
|
17
|
-
from .config.optimized_element_map import OptimizedElementMapGenerator
|
18
16
|
from .config.shared import CameraShared
|
19
17
|
from .config.types import (
|
20
18
|
COLORS,
|
@@ -22,19 +20,15 @@ from .config.types import (
|
|
22
20
|
CalibrationPoints,
|
23
21
|
Colors,
|
24
22
|
RoomsProperties,
|
25
|
-
RoomStore,
|
26
23
|
)
|
27
24
|
from .config.utils import (
|
28
25
|
BaseHandler,
|
29
|
-
get_element_at_position,
|
30
|
-
get_room_at_position,
|
31
26
|
initialize_drawing_config,
|
32
27
|
manage_drawable_elements,
|
33
28
|
prepare_resize_params,
|
34
|
-
update_element_map_with_robot,
|
35
29
|
)
|
36
|
-
from .config.room_outline import extract_room_outline_with_scipy
|
37
30
|
from .hypfer_draw import ImageDraw as ImDraw
|
31
|
+
from .hypfer_rooms_handler import HypferRoomsHandler
|
38
32
|
from .map_data import ImageData
|
39
33
|
|
40
34
|
|
@@ -63,123 +57,62 @@ class HypferMapImageHandler(BaseHandler, AutoCrop):
|
|
63
57
|
self.imd = ImDraw(self) # Image Draw class.
|
64
58
|
self.color_grey = (128, 128, 128, 255)
|
65
59
|
self.file_name = self.shared.file_name # file name of the vacuum.
|
66
|
-
self.
|
67
|
-
self.
|
68
|
-
) #
|
60
|
+
self.rooms_handler = HypferRoomsHandler(
|
61
|
+
self.file_name, self.drawing_config
|
62
|
+
) # Room data handler
|
69
63
|
|
70
64
|
@staticmethod
|
71
65
|
def get_corners(x_max, x_min, y_max, y_min):
|
72
66
|
"""Get the corners of the room."""
|
73
67
|
return [(x_min, y_min), (x_max, y_min), (x_max, y_max), (x_min, y_max)]
|
74
68
|
|
75
|
-
async def extract_room_outline_from_map(self, room_id_int, pixels, pixel_size):
|
76
|
-
"""Extract the outline of a room using the pixel data and element map.
|
77
|
-
|
78
|
-
Args:
|
79
|
-
room_id_int: The room ID as an integer
|
80
|
-
pixels: List of pixel coordinates in the format [[x, y, z], ...]
|
81
|
-
pixel_size: Size of each pixel
|
82
|
-
|
83
|
-
Returns:
|
84
|
-
List of points forming the outline of the room
|
85
|
-
"""
|
86
|
-
# Calculate x and y min/max from compressed pixels for rectangular fallback
|
87
|
-
x_values = []
|
88
|
-
y_values = []
|
89
|
-
for x, y, z in pixels:
|
90
|
-
for i in range(z):
|
91
|
-
x_values.append(x + i * pixel_size)
|
92
|
-
y_values.append(y)
|
93
|
-
|
94
|
-
if not x_values or not y_values:
|
95
|
-
return []
|
96
|
-
|
97
|
-
min_x, max_x = min(x_values), max(x_values)
|
98
|
-
min_y, max_y = min(y_values), max(y_values)
|
99
|
-
|
100
|
-
# If we don't have an element map, return a rectangular outline
|
101
|
-
if not hasattr(self, "element_map") or self.shared.element_map is None:
|
102
|
-
# Return rectangular outline
|
103
|
-
return [(min_x, min_y), (max_x, min_y), (max_x, max_y), (min_x, max_y)]
|
104
|
-
|
105
|
-
# Create a binary mask for this room using the pixel data
|
106
|
-
# This is more reliable than using the element_map since we're directly using the pixel data
|
107
|
-
height, width = self.shared.element_map.shape
|
108
|
-
room_mask = np.zeros((height, width), dtype=np.uint8)
|
109
|
-
|
110
|
-
# Fill the mask with room pixels using the pixel data
|
111
|
-
for x, y, z in pixels:
|
112
|
-
for i in range(z):
|
113
|
-
px = x + i * pixel_size
|
114
|
-
py = y
|
115
|
-
# Make sure we're within bounds
|
116
|
-
if 0 <= py < height and 0 <= px < width:
|
117
|
-
# Mark a pixel_size x pixel_size block in the mask
|
118
|
-
for dx in range(pixel_size):
|
119
|
-
for dy in range(pixel_size):
|
120
|
-
if py + dy < height and px + dx < width:
|
121
|
-
room_mask[py + dy, px + dx] = 1
|
122
|
-
|
123
|
-
# Debug log to check if we have any room pixels
|
124
|
-
num_room_pixels = np.sum(room_mask)
|
125
|
-
LOGGER.debug(
|
126
|
-
"%s: Room %s mask has %d pixels",
|
127
|
-
self.file_name,
|
128
|
-
str(room_id_int),
|
129
|
-
int(num_room_pixels),
|
130
|
-
)
|
131
|
-
|
132
|
-
# Use the scipy-based room outline extraction
|
133
|
-
return await extract_room_outline_with_scipy(
|
134
|
-
room_mask, min_x, min_y, max_x, max_y, self.file_name, room_id_int
|
135
|
-
)
|
136
|
-
|
137
69
|
async def async_extract_room_properties(self, json_data) -> RoomsProperties:
|
138
70
|
"""Extract room properties from the JSON data."""
|
139
71
|
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
72
|
+
return await self.rooms_handler.async_extract_room_properties(json_data)
|
73
|
+
# room_properties = {}
|
74
|
+
# self.rooms_pos = []
|
75
|
+
# pixel_size = json_data.get("pixelSize", [])
|
76
|
+
#
|
77
|
+
# for layer in json_data.get("layers", []):
|
78
|
+
# if layer["__class"] == "MapLayer":
|
79
|
+
# meta_data = layer.get("metaData", {})
|
80
|
+
# segment_id = meta_data.get("segmentId")
|
81
|
+
# if segment_id is not None:
|
82
|
+
# name = meta_data.get("name")
|
83
|
+
# compressed_pixels = layer.get("compressedPixels", [])
|
84
|
+
# pixels = self.data.sublist(compressed_pixels, 3)
|
85
|
+
# # Calculate x and y min/max from compressed pixels
|
86
|
+
# (
|
87
|
+
# x_min,
|
88
|
+
# y_min,
|
89
|
+
# x_max,
|
90
|
+
# y_max,
|
91
|
+
# ) = await self.data.async_get_rooms_coordinates(pixels, pixel_size)
|
92
|
+
# corners = self.get_corners(x_max, x_min, y_max, y_min)
|
93
|
+
# room_id = str(segment_id)
|
94
|
+
# self.rooms_pos.append(
|
95
|
+
# {
|
96
|
+
# "name": name,
|
97
|
+
# "corners": corners,
|
98
|
+
# }
|
99
|
+
# )
|
100
|
+
# room_properties[room_id] = {
|
101
|
+
# "number": segment_id,
|
102
|
+
# "outline": corners,
|
103
|
+
# "name": name,
|
104
|
+
# "x": ((x_min + x_max) // 2),
|
105
|
+
# "y": ((y_min + y_max) // 2),
|
106
|
+
# }
|
107
|
+
# if room_properties:
|
108
|
+
# rooms = RoomStore(self.file_name, room_properties)
|
109
|
+
# LOGGER.debug(
|
110
|
+
# "%s: Rooms data extracted! %s", self.file_name, rooms.get_rooms()
|
111
|
+
# )
|
112
|
+
# else:
|
113
|
+
# LOGGER.debug("%s: Rooms data not available!", self.file_name)
|
114
|
+
# self.rooms_pos = None
|
115
|
+
# return room_properties
|
183
116
|
|
184
117
|
# noinspection PyUnresolvedReferences,PyUnboundLocalVariable
|
185
118
|
async def async_get_image_from_json(
|
@@ -471,36 +404,7 @@ class HypferMapImageHandler(BaseHandler, AutoCrop):
|
|
471
404
|
if img_np_array is None:
|
472
405
|
LOGGER.warning("%s: Image array is None.", self.file_name)
|
473
406
|
return None
|
474
|
-
# Debug logging for element map creation
|
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
|
-
)
|
481
407
|
|
482
|
-
if (self.shared.element_map is None) and (self.frame_number == 1):
|
483
|
-
# Create element map for tracking what's drawn where
|
484
|
-
LOGGER.info(
|
485
|
-
"%s: Creating element map with shape: %s",
|
486
|
-
self.file_name,
|
487
|
-
img_np_array.shape,
|
488
|
-
)
|
489
|
-
|
490
|
-
# Generate the element map directly from JSON data
|
491
|
-
# This will create a cropped element map containing only the non-zero elements
|
492
|
-
LOGGER.info("%s: Generating element map from JSON data", self.file_name)
|
493
|
-
self.shared.element_map = (
|
494
|
-
await self.element_map_manager.async_generate_from_json(m_json)
|
495
|
-
)
|
496
|
-
|
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
|
-
)
|
504
408
|
# Convert the numpy array to a PIL image
|
505
409
|
pil_img = Image.fromarray(img_np_array, mode="RGBA")
|
506
410
|
del img_np_array
|
@@ -584,17 +488,6 @@ class HypferMapImageHandler(BaseHandler, AutoCrop):
|
|
584
488
|
value=value,
|
585
489
|
)
|
586
490
|
|
587
|
-
def get_element_at_position(self, x: int, y: int) -> DrawableElement | None:
|
588
|
-
"""Get the element code at a specific position."""
|
589
|
-
|
590
|
-
return get_element_at_position(self.shared.element_map, x, y)
|
591
|
-
|
592
|
-
def get_room_at_position(self, x: int, y: int) -> int | None:
|
593
|
-
"""Get the room ID at a specific position, or None if not a room."""
|
594
|
-
return get_room_at_position(
|
595
|
-
self.shared.element_map, x, y, DrawableElement.ROOM_1
|
596
|
-
)
|
597
|
-
|
598
491
|
@staticmethod
|
599
492
|
async def async_copy_array(original_array):
|
600
493
|
"""Copy the array."""
|