valetudo-map-parser 0.1.9b50__py3-none-any.whl → 0.1.9b52__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.
@@ -305,679 +305,3 @@ class DrawingConfig:
305
305
  LOGGER.info(
306
306
  "Disabled ROOM_%d element from device_info setting", room_id
307
307
  )
308
-
309
-
310
- class ElementMapGenerator:
311
- """Class for generating 2D element maps from JSON data.
312
-
313
- This class creates a 2D array where each cell contains an integer code
314
- representing the element at that position (floor, wall, room, etc.).
315
- It focuses only on the static structure (rooms and walls).
316
- """
317
-
318
- def __init__(self, drawing_config: DrawingConfig = None, shared_data=None):
319
- """Initialize the element map generator.
320
-
321
- Args:
322
- drawing_config: Optional drawing configuration for element properties
323
- """
324
- self.drawing_config = drawing_config or DrawingConfig()
325
- self.shared = shared_data
326
- self.element_map = None
327
-
328
- async def async_generate_from_json(self, json_data, existing_element_map=None):
329
- """Generate a 2D element map from JSON data without visual rendering.
330
-
331
- Args:
332
- json_data: The JSON data from the vacuum
333
- existing_element_map: Optional pre-created element map to populate
334
-
335
- Returns:
336
- numpy.ndarray: The 2D element map array
337
- """
338
- if not self.shared:
339
- LOGGER.warning("Shared data not provided, some features may not work.")
340
- return None
341
-
342
- # Use existing element map if provided
343
- if existing_element_map is not None:
344
- self.element_map = existing_element_map
345
-
346
- # Check if this is a Valetudo map or a Rand256 map
347
- is_valetudo = (
348
- "size" in json_data and "pixelSize" in json_data and "layers" in json_data
349
- )
350
- is_rand256 = "image" in json_data and "map_data" in json_data
351
-
352
- # Debug logging
353
- LOGGER.debug(f"JSON data keys: {list(json_data.keys())}")
354
- LOGGER.debug(f"Is Valetudo: {is_valetudo}, Is Rand256: {is_rand256}")
355
-
356
- # Create element map if not provided
357
- if self.element_map is None:
358
- if is_valetudo:
359
- # Get map dimensions from Valetudo map
360
- map_size = json_data.get("size", 0)
361
- if isinstance(map_size, dict):
362
- # If map_size is a dictionary, extract the values
363
- size_x = map_size.get("width", 0)
364
- size_y = map_size.get("height", 0)
365
- else:
366
- # If map_size is a number, use it for both dimensions
367
- pixel_size = json_data.get(
368
- "pixelSize", 5
369
- ) # Default to 5mm per pixel
370
- size_x = int(map_size // pixel_size)
371
- size_y = int(map_size // pixel_size)
372
- self.element_map = np.zeros((size_y, size_x), dtype=np.int32)
373
- self.element_map[:] = DrawableElement.FLOOR
374
- elif is_rand256:
375
- # Get map dimensions from Rand256 map
376
- map_data = json_data.get("map_data", {})
377
- size_x = map_data.get("width", 0)
378
- size_y = map_data.get("height", 0)
379
- self.element_map = np.zeros((size_y, size_x), dtype=np.int32)
380
-
381
- if not (is_valetudo or is_rand256):
382
- LOGGER.error("Unknown JSON format, cannot generate element map")
383
- return None
384
-
385
- if is_valetudo:
386
- # Get map dimensions from the Valetudo JSON data
387
- size_x = json_data["size"]["x"]
388
- size_y = json_data["size"]["y"]
389
- pixel_size = json_data["pixelSize"]
390
-
391
- # Calculate scale factor based on pixel size (normalize to 5mm standard)
392
- # This helps handle maps with different scales
393
- scale_factor = pixel_size if pixel_size != 0 else 1.0
394
- LOGGER.info(
395
- f"Map dimensions: {size_x}x{size_y}, pixel size: {pixel_size}mm, scale factor: {scale_factor:.2f}"
396
- )
397
-
398
- # Ensure element_map is properly initialized with the correct dimensions
399
- if (
400
- self.element_map is None
401
- or self.element_map.shape[0] == 0
402
- or self.element_map.shape[1] == 0
403
- ):
404
- # For now, create a full-sized element map to ensure coordinates match
405
- # We'll resize it at the end for efficiency
406
- map_width = int(size_x // pixel_size) if pixel_size != 0 else size_x
407
- map_height = int(size_y // pixel_size) if pixel_size != 0 else size_y
408
-
409
- LOGGER.info(
410
- f"Creating element map with dimensions: {map_width}x{map_height}"
411
- )
412
- self.element_map = np.zeros((map_height, map_width), dtype=np.int32)
413
- self.element_map[:] = DrawableElement.FLOOR
414
-
415
- # Process layers (rooms, walls, etc.)
416
- for layer in json_data["layers"]:
417
- layer_type = layer["type"]
418
-
419
- # Process rooms (segments)
420
- if layer_type == "segment":
421
- # Handle different segment formats
422
- if "segments" in layer:
423
- segments = layer["segments"]
424
- else:
425
- segments = [
426
- layer
427
- ] # Some formats have segment data directly in the layer
428
-
429
- for segment in segments:
430
- # Get room ID and check if it's enabled
431
- if "id" in segment:
432
- room_id = segment["id"]
433
- room_id_int = int(room_id)
434
- elif (
435
- "metaData" in segment and "segmentId" in segment["metaData"]
436
- ):
437
- # Handle Hypfer format
438
- room_id = segment["metaData"]["segmentId"]
439
- room_id_int = int(room_id)
440
- else:
441
- # Skip segments without ID
442
- continue
443
-
444
- # Skip if room is disabled
445
- room_element = getattr(
446
- DrawableElement, f"ROOM_{room_id_int}", None
447
- )
448
- if room_element is None or not self.drawing_config.is_enabled(
449
- room_element
450
- ):
451
- continue
452
-
453
- # Room element code was already retrieved above
454
- if room_element is not None:
455
- # Process pixels for this room
456
- if "pixels" in segment and segment["pixels"]:
457
- # Regular pixel format
458
- for x, y, z in segment["pixels"]:
459
- # Calculate pixel coordinates
460
- col = x * pixel_size
461
- row = y * pixel_size
462
-
463
- # Fill the element map with room code
464
- for i in range(z):
465
- # Get the region to update
466
- region_col_start = col + i * pixel_size
467
- region_col_end = col + (i + 1) * pixel_size
468
- region_row_start = row
469
- region_row_end = row + pixel_size
470
-
471
- # Update element map for this region
472
- if (
473
- region_row_start < size_y
474
- and region_col_start < size_x
475
- ):
476
- # Ensure we stay within bounds
477
- end_row = min(region_row_end, size_y)
478
- end_col = min(region_col_end, size_x)
479
-
480
- # Set element code for this region
481
- # Only set pixels that are not already set (floor is 1)
482
- region = self.element_map[
483
- region_row_start:end_row,
484
- region_col_start:end_col,
485
- ]
486
- mask = region == DrawableElement.FLOOR
487
- region[mask] = room_element
488
-
489
- elif (
490
- "compressedPixels" in segment
491
- and segment["compressedPixels"]
492
- ):
493
- # Compressed pixel format (used in Valetudo)
494
- compressed_pixels = segment["compressedPixels"]
495
- i = 0
496
- pixel_count = 0
497
-
498
- while i < len(compressed_pixels):
499
- x = compressed_pixels[i]
500
- y = compressed_pixels[i + 1]
501
- count = compressed_pixels[i + 2]
502
- pixel_count += count
503
-
504
- # Set element code for this run of pixels
505
- for j in range(count):
506
- px = x + j
507
- if 0 <= px < size_x and 0 <= y < size_y:
508
- self.element_map[y, px] = room_element
509
-
510
- i += 3
511
-
512
- # Debug: Log that we're adding room pixels
513
- LOGGER.info(
514
- f"Adding room {room_id_int} pixels to element map with code {room_element}"
515
- )
516
- LOGGER.info(
517
- f"Room {room_id_int} has {len(compressed_pixels) // 3} compressed runs with {pixel_count} total pixels"
518
- )
519
-
520
- # Process walls
521
- elif layer_type == "wall":
522
- # Skip if walls are disabled
523
- if not self.drawing_config.is_enabled(DrawableElement.WALL):
524
- continue
525
-
526
- # Process wall pixels
527
- if "pixels" in layer and layer["pixels"]:
528
- # Regular pixel format
529
- for x, y, z in layer["pixels"]:
530
- # Calculate pixel coordinates
531
- col = x * pixel_size
532
- row = y * pixel_size
533
-
534
- # Fill the element map with wall code
535
- for i in range(z):
536
- # Get the region to update
537
- region_col_start = col + i * pixel_size
538
- region_col_end = col + (i + 1) * pixel_size
539
- region_row_start = row
540
- region_row_end = row + pixel_size
541
-
542
- # Update element map for this region
543
- if (
544
- region_row_start < size_y
545
- and region_col_start < size_x
546
- ):
547
- # Ensure we stay within bounds
548
- end_row = min(region_row_end, size_y)
549
- end_col = min(region_col_end, size_x)
550
-
551
- # Set element code for this region
552
- # Only set pixels that are not already set (floor is 1)
553
- region = self.element_map[
554
- region_row_start:end_row,
555
- region_col_start:end_col,
556
- ]
557
- mask = region == DrawableElement.FLOOR
558
- region[mask] = DrawableElement.WALL
559
-
560
- elif "compressedPixels" in layer and layer["compressedPixels"]:
561
- # Compressed pixel format (used in Valetudo)
562
- compressed_pixels = layer["compressedPixels"]
563
- i = 0
564
- pixel_count = 0
565
-
566
- while i < len(compressed_pixels):
567
- x = compressed_pixels[i]
568
- y = compressed_pixels[i + 1]
569
- count = compressed_pixels[i + 2]
570
- pixel_count += count
571
-
572
- # Set element code for this run of pixels
573
- for j in range(count):
574
- px = x + j
575
- if 0 <= px < size_x and 0 <= y < size_y:
576
- self.element_map[y, px] = DrawableElement.WALL
577
-
578
- i += 3
579
-
580
- # Debug: Log that we're adding wall pixels
581
- LOGGER.info(
582
- f"Adding wall pixels to element map with code {DrawableElement.WALL}"
583
- )
584
- LOGGER.info(
585
- f"Wall layer has {len(compressed_pixels) // 3} compressed runs with {pixel_count} total pixels"
586
- )
587
-
588
- elif is_rand256:
589
- # Get map dimensions from the Rand256 JSON data
590
- map_data = json_data["map_data"]
591
- size_x = map_data["dimensions"]["width"]
592
- size_y = map_data["dimensions"]["height"]
593
-
594
- # Create empty element map initialized with floor
595
- self.element_map = np.zeros((size_y, size_x), dtype=np.int32)
596
- self.element_map[:] = DrawableElement.FLOOR
597
-
598
- # Process rooms
599
- if "rooms" in map_data and map_data["rooms"]:
600
- for room in map_data["rooms"]:
601
- # Get room ID and check if it's enabled
602
- room_id_int = room["id"]
603
-
604
- # Skip if room is disabled
605
- if not self.drawing_config.is_enabled(f"ROOM_{room_id_int}"):
606
- continue
607
-
608
- # Get room element code (ROOM_1, ROOM_2, etc.)
609
- room_element = None
610
- if 0 < room_id_int <= 15:
611
- room_element = getattr(
612
- DrawableElement, f"ROOM_{room_id_int}", None
613
- )
614
-
615
- if room_element is not None and "coordinates" in room:
616
- # Process coordinates for this room
617
- for coord in room["coordinates"]:
618
- x, y = coord
619
- # Update element map for this pixel
620
- if 0 <= y < size_y and 0 <= x < size_x:
621
- self.element_map[y, x] = room_element
622
-
623
- # Process segments (alternative format for rooms)
624
- if "segments" in map_data and map_data["segments"]:
625
- for segment_id, coordinates in map_data["segments"].items():
626
- # Get room ID and check if it's enabled
627
- room_id_int = int(segment_id)
628
-
629
- # Skip if room is disabled
630
- if not self.drawing_config.is_enabled(f"ROOM_{room_id_int}"):
631
- continue
632
-
633
- # Get room element code (ROOM_1, ROOM_2, etc.)
634
- room_element = None
635
- if 0 < room_id_int <= 15:
636
- room_element = getattr(
637
- DrawableElement, f"ROOM_{room_id_int}", None
638
- )
639
-
640
- if room_element is not None and coordinates:
641
- # Process individual coordinates
642
- for coord in coordinates:
643
- if isinstance(coord, (list, tuple)) and len(coord) == 2:
644
- x, y = coord
645
- # Update element map for this pixel
646
- if 0 <= y < size_y and 0 <= x < size_x:
647
- self.element_map[y, x] = room_element
648
-
649
- # Process walls
650
- if "walls" in map_data and map_data["walls"]:
651
- # Skip if walls are disabled
652
- if self.drawing_config.is_element_enabled("WALL"):
653
- # Process wall coordinates
654
- for coord in map_data["walls"]:
655
- x, y = coord
656
- # Update element map for this pixel
657
- if 0 <= y < size_y and 0 <= x < size_x:
658
- self.element_map[y, x] = DrawableElement.WALL
659
-
660
- # Find the bounding box of non-zero elements to crop the element map
661
- non_zero_indices = np.nonzero(self.element_map)
662
- if len(non_zero_indices[0]) > 0: # If there are any non-zero elements
663
- # Get the bounding box coordinates
664
- min_y, max_y = np.min(non_zero_indices[0]), np.max(non_zero_indices[0])
665
- min_x, max_x = np.min(non_zero_indices[1]), np.max(non_zero_indices[1])
666
-
667
- # Add a margin around the bounding box
668
- margin = 20 # Pixels of margin around the non-zero elements
669
- crop_min_y = max(0, min_y - margin)
670
- crop_max_y = min(self.element_map.shape[0] - 1, max_y + margin)
671
- crop_min_x = max(0, min_x - margin)
672
- crop_max_x = min(self.element_map.shape[1] - 1, max_x + margin)
673
-
674
- # Log the cropping information
675
- LOGGER.info(
676
- f"Cropping element map from {self.element_map.shape} to bounding box: "
677
- f"({crop_min_x}, {crop_min_y}) to ({crop_max_x}, {crop_max_y})"
678
- )
679
- LOGGER.info(
680
- f"Cropped dimensions: {crop_max_x - crop_min_x + 1}x{crop_max_y - crop_min_y + 1}"
681
- )
682
-
683
- # Create a new, smaller array with just the non-zero region
684
- cropped_map = self.element_map[
685
- crop_min_y : crop_max_y + 1, crop_min_x : crop_max_x + 1
686
- ].copy()
687
-
688
- # Store the cropping coordinates in the shared data for later reference
689
- if self.shared:
690
- self.shared.element_map_crop = {
691
- "min_x": crop_min_x,
692
- "min_y": crop_min_y,
693
- "max_x": crop_max_x,
694
- "max_y": crop_max_y,
695
- "original_shape": self.element_map.shape,
696
- }
697
-
698
- # Replace the element map with the cropped version
699
- self.element_map = cropped_map
700
-
701
- # Now resize the element map to reduce its dimensions
702
- # Calculate the resize factor based on the current size
703
- resize_factor = 5 # Reduce to 1/5 of the current size
704
- new_height = max(
705
- self.element_map.shape[0] // resize_factor, 50
706
- ) # Ensure minimum size
707
- new_width = max(
708
- self.element_map.shape[1] // resize_factor, 50
709
- ) # Ensure minimum size
710
-
711
- # Create a resized element map
712
- resized_map = np.zeros((new_height, new_width), dtype=np.int32)
713
- resized_map[:] = DrawableElement.FLOOR # Initialize with floor
714
-
715
- # Calculate scaling factors
716
- y_scale = self.element_map.shape[0] / new_height
717
- x_scale = self.element_map.shape[1] / new_width
718
-
719
- # Populate the resized map by sampling from the original map
720
- for y in range(new_height):
721
- for x in range(new_width):
722
- # Get the corresponding position in the original map
723
- orig_y = min(int(y * y_scale), self.element_map.shape[0] - 1)
724
- orig_x = min(int(x * x_scale), self.element_map.shape[1] - 1)
725
- # Copy the element code
726
- resized_map[y, x] = self.element_map[orig_y, orig_x]
727
-
728
- # Store the resize information in shared data
729
- if self.shared:
730
- if hasattr(self.shared, "element_map_crop"):
731
- self.shared.element_map_crop["resize_factor"] = resize_factor
732
- self.shared.element_map_crop["resized_shape"] = resized_map.shape
733
- self.shared.element_map_crop["original_cropped_shape"] = (
734
- self.element_map.shape
735
- )
736
-
737
- # Log the resizing information
738
- LOGGER.info(
739
- f"Resized element map from {self.element_map.shape} to {resized_map.shape} (1/{resize_factor} of cropped size)"
740
- )
741
-
742
- # Replace the element map with the resized version
743
- self.element_map = resized_map
744
-
745
- return self.element_map
746
-
747
- def get_element_map(self):
748
- """Return the element map.
749
-
750
- Returns:
751
- numpy.ndarray: The 2D element map array or None if not initialized
752
- """
753
- return self.element_map
754
-
755
- def get_element_at_image_position(self, x: int, y: int):
756
- """Get the element code at the specified position in the image.
757
-
758
- This method uses calibration points to accurately map between image coordinates
759
- and element map coordinates.
760
-
761
- Args:
762
- x: X coordinate in the image (e.g., 0-1984)
763
- y: Y coordinate in the image (e.g., 0-1824)
764
-
765
- Returns:
766
- Element code at the specified position, or None if out of bounds
767
- """
768
- if self.shared is None or self.element_map is None:
769
- return None
770
-
771
- # Get calibration points if available
772
- calibration_points = None
773
- if hasattr(self.shared, "attr_calibration_points"):
774
- calibration_points = self.shared.attr_calibration_points
775
-
776
- if calibration_points and len(calibration_points) >= 4:
777
- # Extract image and vacuum coordinates from calibration points
778
- image_points = []
779
- vacuum_points = []
780
- for point in calibration_points:
781
- if "map" in point and "vacuum" in point:
782
- image_points.append((point["map"]["x"], point["map"]["y"]))
783
- vacuum_points.append((point["vacuum"]["x"], point["vacuum"]["y"]))
784
-
785
- if len(image_points) >= 2:
786
- # Calculate scaling factors
787
- img_x_min = min(p[0] for p in image_points)
788
- img_x_max = max(p[0] for p in image_points)
789
- img_y_min = min(p[1] for p in image_points)
790
- img_y_max = max(p[1] for p in image_points)
791
-
792
- vac_x_min = min(p[0] for p in vacuum_points)
793
- vac_x_max = max(p[0] for p in vacuum_points)
794
- vac_y_min = min(p[1] for p in vacuum_points)
795
- vac_y_max = max(p[1] for p in vacuum_points)
796
-
797
- # Normalize the input coordinates to 0-1 range in image space
798
- norm_x = (
799
- (x - img_x_min) / (img_x_max - img_x_min)
800
- if img_x_max > img_x_min
801
- else 0
802
- )
803
- norm_y = (
804
- (y - img_y_min) / (img_y_max - img_y_min)
805
- if img_y_max > img_y_min
806
- else 0
807
- )
808
-
809
- # Map to vacuum coordinates
810
- vac_x = vac_x_min + norm_x * (vac_x_max - vac_x_min)
811
- vac_y = vac_y_min + norm_y * (vac_y_max - vac_y_min)
812
-
813
- LOGGER.debug(
814
- f"Mapped image ({x}, {y}) to vacuum ({vac_x:.1f}, {vac_y:.1f})"
815
- )
816
-
817
- # Now map from vacuum coordinates to element map coordinates
818
- # This depends on how the element map was created
819
- if (
820
- hasattr(self.shared, "element_map_crop")
821
- and self.shared.element_map_crop
822
- ):
823
- crop_info = self.shared.element_map_crop
824
-
825
- # Adjust for cropping
826
- if "min_x" in crop_info and "min_y" in crop_info:
827
- elem_x = int(vac_x - crop_info["min_x"])
828
- elem_y = int(vac_y - crop_info["min_y"])
829
-
830
- # Adjust for resizing
831
- if (
832
- "resize_factor" in crop_info
833
- and "original_cropped_shape" in crop_info
834
- and "resized_shape" in crop_info
835
- ):
836
- orig_h, orig_w = crop_info["original_cropped_shape"]
837
- resized_h, resized_w = crop_info["resized_shape"]
838
-
839
- # Scale to resized coordinates
840
- elem_x = int(elem_x * resized_w / orig_w)
841
- elem_y = int(elem_y * resized_h / orig_h)
842
-
843
- LOGGER.debug(
844
- f"Mapped vacuum ({vac_x:.1f}, {vac_y:.1f}) to element map ({elem_x}, {elem_y})"
845
- )
846
-
847
- # Check bounds and return element
848
- height, width = self.element_map.shape
849
- if 0 <= elem_y < height and 0 <= elem_x < width:
850
- return self.element_map[elem_y, elem_x]
851
-
852
- # Fallback to the simpler method if calibration points aren't available
853
- return self.get_element_at_position(x, y, is_image_coords=True)
854
-
855
- def get_element_name(self, element_code):
856
- """Get the name of the element from its code.
857
-
858
- Args:
859
- element_code: The element code (e.g., 1, 2, 101, etc.)
860
-
861
- Returns:
862
- The name of the element (e.g., 'FLOOR', 'WALL', 'ROOM_1', etc.)
863
- """
864
- if element_code is None:
865
- return "NONE"
866
-
867
- # Check if it's a room
868
- if element_code >= 100:
869
- room_number = element_code - 100
870
- return f"ROOM_{room_number}"
871
-
872
- # Check standard elements
873
- for name, code in vars(DrawableElement).items():
874
- if (
875
- not name.startswith("_")
876
- and isinstance(code, int)
877
- and code == element_code
878
- ):
879
- return name
880
-
881
- return f"UNKNOWN_{element_code}"
882
-
883
- def get_element_at_position(self, x: int, y: int, is_image_coords: bool = False):
884
- """Get the element code at the specified position in the element map.
885
-
886
- Args:
887
- x: X coordinate in the original (uncropped) element map or image
888
- y: Y coordinate in the original (uncropped) element map or image
889
- is_image_coords: If True, x and y are image coordinates (e.g., 1984x1824)
890
- If False, x and y are element map coordinates
891
-
892
- Returns:
893
- Element code at the specified position, or None if out of bounds
894
- """
895
- if self.element_map is None:
896
- return None
897
-
898
- # If coordinates are from the image, convert them to element map coordinates first
899
- if is_image_coords and self.shared:
900
- # Get image dimensions
901
- if (
902
- hasattr(self.shared, "image_size")
903
- and self.shared.image_size is not None
904
- and len(self.shared.image_size) >= 2
905
- ):
906
- image_width = self.shared.image_size[0]
907
- image_height = self.shared.image_size[1]
908
- else:
909
- # Default image dimensions if not available
910
- image_width = 1984
911
- image_height = 1824
912
-
913
- # Get original element map dimensions (before resizing)
914
- if (
915
- hasattr(self.shared, "element_map_crop")
916
- and self.shared.element_map_crop is not None
917
- and "original_cropped_shape" in self.shared.element_map_crop
918
- ):
919
- original_map_height, original_map_width = self.shared.element_map_crop[
920
- "original_cropped_shape"
921
- ]
922
- else:
923
- # Estimate based on typical values
924
- original_map_width = 1310
925
- original_map_height = 1310
926
-
927
- # Calculate scaling factors between image and original element map
928
- x_scale_to_map = original_map_width / image_width
929
- y_scale_to_map = original_map_height / image_height
930
-
931
- # Convert image coordinates to element map coordinates
932
- # Apply a small offset to better align with the actual elements
933
- # This is based on empirical testing with the sample coordinates
934
- x_offset = 50 # Adjust as needed based on testing
935
- y_offset = 20 # Adjust as needed based on testing
936
- x = int((x + x_offset) * x_scale_to_map)
937
- y = int((y + y_offset) * y_scale_to_map)
938
-
939
- LOGGER.debug(
940
- f"Converted image coordinates ({x}, {y}) to element map coordinates"
941
- )
942
-
943
- # Adjust coordinates if the element map has been cropped and resized
944
- if self.shared and hasattr(self.shared, "element_map_crop"):
945
- # Get the crop information
946
- crop_info = self.shared.element_map_crop
947
-
948
- # Adjust coordinates to the cropped map
949
- x_cropped = x - crop_info["min_x"]
950
- y_cropped = y - crop_info["min_y"]
951
-
952
- # If the map has been resized, adjust coordinates further
953
- if "resize_factor" in crop_info:
954
- resize_factor = crop_info["resize_factor"]
955
- original_cropped_shape = crop_info.get("original_cropped_shape")
956
- resized_shape = crop_info.get("resized_shape")
957
-
958
- if original_cropped_shape and resized_shape:
959
- # Calculate scaling factors
960
- y_scale = original_cropped_shape[0] / resized_shape[0]
961
- x_scale = original_cropped_shape[1] / resized_shape[1]
962
-
963
- # Scale the coordinates to the resized map
964
- x_resized = int(x_cropped / x_scale)
965
- y_resized = int(y_cropped / y_scale)
966
-
967
- # Check if the coordinates are within the resized map
968
- height, width = self.element_map.shape
969
- if 0 <= y_resized < height and 0 <= x_resized < width:
970
- return self.element_map[y_resized, x_resized]
971
- return None
972
-
973
- # If no resizing or missing resize info, use cropped coordinates
974
- height, width = self.element_map.shape
975
- if 0 <= y_cropped < height and 0 <= x_cropped < width:
976
- return self.element_map[y_cropped, x_cropped]
977
- return None
978
- else:
979
- # No cropping, use coordinates as is
980
- height, width = self.element_map.shape
981
- if 0 <= y < height and 0 <= x < width:
982
- return self.element_map[y, x]
983
- return None