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.
@@ -344,7 +344,9 @@ class ElementMapGenerator:
344
344
  self.element_map = existing_element_map
345
345
 
346
346
  # Check if this is a Valetudo map or a Rand256 map
347
- is_valetudo = "size" in json_data and "pixelSize" in json_data and "layers" in json_data
347
+ is_valetudo = (
348
+ "size" in json_data and "pixelSize" in json_data and "layers" in json_data
349
+ )
348
350
  is_rand256 = "image" in json_data and "map_data" in json_data
349
351
 
350
352
  # Debug logging
@@ -362,7 +364,9 @@ class ElementMapGenerator:
362
364
  size_y = map_size.get("height", 0)
363
365
  else:
364
366
  # If map_size is a number, use it for both dimensions
365
- pixel_size = json_data.get("pixelSize", 5) # Default to 5mm per pixel
367
+ pixel_size = json_data.get(
368
+ "pixelSize", 5
369
+ ) # Default to 5mm per pixel
366
370
  size_x = int(map_size // pixel_size)
367
371
  size_y = int(map_size // pixel_size)
368
372
  self.element_map = np.zeros((size_y, size_x), dtype=np.int32)
@@ -387,16 +391,24 @@ class ElementMapGenerator:
387
391
  # Calculate scale factor based on pixel size (normalize to 5mm standard)
388
392
  # This helps handle maps with different scales
389
393
  scale_factor = pixel_size if pixel_size != 0 else 1.0
390
- LOGGER.info(f"Map dimensions: {size_x}x{size_y}, pixel size: {pixel_size}mm, scale factor: {scale_factor:.2f}")
394
+ LOGGER.info(
395
+ f"Map dimensions: {size_x}x{size_y}, pixel size: {pixel_size}mm, scale factor: {scale_factor:.2f}"
396
+ )
391
397
 
392
398
  # Ensure element_map is properly initialized with the correct dimensions
393
- if self.element_map is None or self.element_map.shape[0] == 0 or self.element_map.shape[1] == 0:
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
+ ):
394
404
  # For now, create a full-sized element map to ensure coordinates match
395
405
  # We'll resize it at the end for efficiency
396
406
  map_width = int(size_x // pixel_size) if pixel_size != 0 else size_x
397
407
  map_height = int(size_y // pixel_size) if pixel_size != 0 else size_y
398
408
 
399
- LOGGER.info(f"Creating element map with dimensions: {map_width}x{map_height}")
409
+ LOGGER.info(
410
+ f"Creating element map with dimensions: {map_width}x{map_height}"
411
+ )
400
412
  self.element_map = np.zeros((map_height, map_width), dtype=np.int32)
401
413
  self.element_map[:] = DrawableElement.FLOOR
402
414
 
@@ -410,14 +422,18 @@ class ElementMapGenerator:
410
422
  if "segments" in layer:
411
423
  segments = layer["segments"]
412
424
  else:
413
- segments = [layer] # Some formats have segment data directly in the layer
425
+ segments = [
426
+ layer
427
+ ] # Some formats have segment data directly in the layer
414
428
 
415
429
  for segment in segments:
416
430
  # Get room ID and check if it's enabled
417
431
  if "id" in segment:
418
432
  room_id = segment["id"]
419
433
  room_id_int = int(room_id)
420
- elif "metaData" in segment and "segmentId" in segment["metaData"]:
434
+ elif (
435
+ "metaData" in segment and "segmentId" in segment["metaData"]
436
+ ):
421
437
  # Handle Hypfer format
422
438
  room_id = segment["metaData"]["segmentId"]
423
439
  room_id_int = int(room_id)
@@ -426,8 +442,12 @@ class ElementMapGenerator:
426
442
  continue
427
443
 
428
444
  # Skip if room is disabled
429
- room_element = getattr(DrawableElement, f"ROOM_{room_id_int}", None)
430
- if room_element is None or not self.drawing_config.is_enabled(room_element):
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
+ ):
431
451
  continue
432
452
 
433
453
  # Room element code was already retrieved above
@@ -449,18 +469,27 @@ class ElementMapGenerator:
449
469
  region_row_end = row + pixel_size
450
470
 
451
471
  # Update element map for this region
452
- if region_row_start < size_y and region_col_start < size_x:
472
+ if (
473
+ region_row_start < size_y
474
+ and region_col_start < size_x
475
+ ):
453
476
  # Ensure we stay within bounds
454
477
  end_row = min(region_row_end, size_y)
455
478
  end_col = min(region_col_end, size_x)
456
479
 
457
480
  # Set element code for this region
458
481
  # Only set pixels that are not already set (floor is 1)
459
- region = self.element_map[region_row_start:end_row, region_col_start:end_col]
482
+ region = self.element_map[
483
+ region_row_start:end_row,
484
+ region_col_start:end_col,
485
+ ]
460
486
  mask = region == DrawableElement.FLOOR
461
487
  region[mask] = room_element
462
488
 
463
- elif "compressedPixels" in segment and segment["compressedPixels"]:
489
+ elif (
490
+ "compressedPixels" in segment
491
+ and segment["compressedPixels"]
492
+ ):
464
493
  # Compressed pixel format (used in Valetudo)
465
494
  compressed_pixels = segment["compressedPixels"]
466
495
  i = 0
@@ -468,8 +497,8 @@ class ElementMapGenerator:
468
497
 
469
498
  while i < len(compressed_pixels):
470
499
  x = compressed_pixels[i]
471
- y = compressed_pixels[i+1]
472
- count = compressed_pixels[i+2]
500
+ y = compressed_pixels[i + 1]
501
+ count = compressed_pixels[i + 2]
473
502
  pixel_count += count
474
503
 
475
504
  # Set element code for this run of pixels
@@ -481,8 +510,12 @@ class ElementMapGenerator:
481
510
  i += 3
482
511
 
483
512
  # Debug: Log that we're adding room pixels
484
- LOGGER.info(f"Adding room {room_id_int} pixels to element map with code {room_element}")
485
- LOGGER.info(f"Room {room_id_int} has {len(compressed_pixels)//3} compressed runs with {pixel_count} total 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
+ )
486
519
 
487
520
  # Process walls
488
521
  elif layer_type == "wall":
@@ -507,14 +540,20 @@ class ElementMapGenerator:
507
540
  region_row_end = row + pixel_size
508
541
 
509
542
  # Update element map for this region
510
- if region_row_start < size_y and region_col_start < size_x:
543
+ if (
544
+ region_row_start < size_y
545
+ and region_col_start < size_x
546
+ ):
511
547
  # Ensure we stay within bounds
512
548
  end_row = min(region_row_end, size_y)
513
549
  end_col = min(region_col_end, size_x)
514
550
 
515
551
  # Set element code for this region
516
552
  # Only set pixels that are not already set (floor is 1)
517
- region = self.element_map[region_row_start:end_row, region_col_start:end_col]
553
+ region = self.element_map[
554
+ region_row_start:end_row,
555
+ region_col_start:end_col,
556
+ ]
518
557
  mask = region == DrawableElement.FLOOR
519
558
  region[mask] = DrawableElement.WALL
520
559
 
@@ -526,8 +565,8 @@ class ElementMapGenerator:
526
565
 
527
566
  while i < len(compressed_pixels):
528
567
  x = compressed_pixels[i]
529
- y = compressed_pixels[i+1]
530
- count = compressed_pixels[i+2]
568
+ y = compressed_pixels[i + 1]
569
+ count = compressed_pixels[i + 2]
531
570
  pixel_count += count
532
571
 
533
572
  # Set element code for this run of pixels
@@ -539,8 +578,12 @@ class ElementMapGenerator:
539
578
  i += 3
540
579
 
541
580
  # Debug: Log that we're adding wall pixels
542
- LOGGER.info(f"Adding wall pixels to element map with code {DrawableElement.WALL}")
543
- LOGGER.info(f"Wall layer has {len(compressed_pixels)//3} compressed runs with {pixel_count} total 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
+ )
544
587
 
545
588
  elif is_rand256:
546
589
  # Get map dimensions from the Rand256 JSON data
@@ -565,7 +608,9 @@ class ElementMapGenerator:
565
608
  # Get room element code (ROOM_1, ROOM_2, etc.)
566
609
  room_element = None
567
610
  if 0 < room_id_int <= 15:
568
- room_element = getattr(DrawableElement, f"ROOM_{room_id_int}", None)
611
+ room_element = getattr(
612
+ DrawableElement, f"ROOM_{room_id_int}", None
613
+ )
569
614
 
570
615
  if room_element is not None and "coordinates" in room:
571
616
  # Process coordinates for this room
@@ -588,7 +633,9 @@ class ElementMapGenerator:
588
633
  # Get room element code (ROOM_1, ROOM_2, etc.)
589
634
  room_element = None
590
635
  if 0 < room_id_int <= 15:
591
- room_element = getattr(DrawableElement, f"ROOM_{room_id_int}", None)
636
+ room_element = getattr(
637
+ DrawableElement, f"ROOM_{room_id_int}", None
638
+ )
592
639
 
593
640
  if room_element is not None and coordinates:
594
641
  # Process individual coordinates
@@ -625,21 +672,27 @@ class ElementMapGenerator:
625
672
  crop_max_x = min(self.element_map.shape[1] - 1, max_x + margin)
626
673
 
627
674
  # Log the cropping information
628
- LOGGER.info(f"Cropping element map from {self.element_map.shape} to bounding box: "
629
- f"({crop_min_x}, {crop_min_y}) to ({crop_max_x}, {crop_max_y})")
630
- LOGGER.info(f"Cropped dimensions: {crop_max_x - crop_min_x + 1}x{crop_max_y - crop_min_y + 1}")
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
+ )
631
682
 
632
683
  # Create a new, smaller array with just the non-zero region
633
- cropped_map = self.element_map[crop_min_y:crop_max_y+1, crop_min_x:crop_max_x+1].copy()
684
+ cropped_map = self.element_map[
685
+ crop_min_y : crop_max_y + 1, crop_min_x : crop_max_x + 1
686
+ ].copy()
634
687
 
635
688
  # Store the cropping coordinates in the shared data for later reference
636
689
  if self.shared:
637
690
  self.shared.element_map_crop = {
638
- 'min_x': crop_min_x,
639
- 'min_y': crop_min_y,
640
- 'max_x': crop_max_x,
641
- 'max_y': crop_max_y,
642
- 'original_shape': self.element_map.shape
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,
643
696
  }
644
697
 
645
698
  # Replace the element map with the cropped version
@@ -648,8 +701,12 @@ class ElementMapGenerator:
648
701
  # Now resize the element map to reduce its dimensions
649
702
  # Calculate the resize factor based on the current size
650
703
  resize_factor = 5 # Reduce to 1/5 of the current size
651
- new_height = max(self.element_map.shape[0] // resize_factor, 50) # Ensure minimum size
652
- new_width = max(self.element_map.shape[1] // resize_factor, 50) # Ensure minimum 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
653
710
 
654
711
  # Create a resized element map
655
712
  resized_map = np.zeros((new_height, new_width), dtype=np.int32)
@@ -670,13 +727,17 @@ class ElementMapGenerator:
670
727
 
671
728
  # Store the resize information in shared data
672
729
  if self.shared:
673
- if hasattr(self.shared, 'element_map_crop'):
674
- self.shared.element_map_crop['resize_factor'] = resize_factor
675
- self.shared.element_map_crop['resized_shape'] = resized_map.shape
676
- self.shared.element_map_crop['original_cropped_shape'] = self.element_map.shape
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
+ )
677
736
 
678
737
  # Log the resizing information
679
- LOGGER.info(f"Resized element map from {self.element_map.shape} to {resized_map.shape} (1/{resize_factor} of cropped size)")
738
+ LOGGER.info(
739
+ f"Resized element map from {self.element_map.shape} to {resized_map.shape} (1/{resize_factor} of cropped size)"
740
+ )
680
741
 
681
742
  # Replace the element map with the resized version
682
743
  self.element_map = resized_map
@@ -709,7 +770,7 @@ class ElementMapGenerator:
709
770
 
710
771
  # Get calibration points if available
711
772
  calibration_points = None
712
- if hasattr(self.shared, 'attr_calibration_points'):
773
+ if hasattr(self.shared, "attr_calibration_points"):
713
774
  calibration_points = self.shared.attr_calibration_points
714
775
 
715
776
  if calibration_points and len(calibration_points) >= 4:
@@ -717,9 +778,9 @@ class ElementMapGenerator:
717
778
  image_points = []
718
779
  vacuum_points = []
719
780
  for point in calibration_points:
720
- if 'map' in point and 'vacuum' in point:
721
- image_points.append((point['map']['x'], point['map']['y']))
722
- vacuum_points.append((point['vacuum']['x'], point['vacuum']['y']))
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"]))
723
784
 
724
785
  if len(image_points) >= 2:
725
786
  # Calculate scaling factors
@@ -734,35 +795,54 @@ class ElementMapGenerator:
734
795
  vac_y_max = max(p[1] for p in vacuum_points)
735
796
 
736
797
  # Normalize the input coordinates to 0-1 range in image space
737
- norm_x = (x - img_x_min) / (img_x_max - img_x_min) if img_x_max > img_x_min else 0
738
- norm_y = (y - img_y_min) / (img_y_max - img_y_min) if img_y_max > img_y_min else 0
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
+ )
739
808
 
740
809
  # Map to vacuum coordinates
741
810
  vac_x = vac_x_min + norm_x * (vac_x_max - vac_x_min)
742
811
  vac_y = vac_y_min + norm_y * (vac_y_max - vac_y_min)
743
812
 
744
- LOGGER.debug(f"Mapped image ({x}, {y}) to vacuum ({vac_x:.1f}, {vac_y:.1f})")
813
+ LOGGER.debug(
814
+ f"Mapped image ({x}, {y}) to vacuum ({vac_x:.1f}, {vac_y:.1f})"
815
+ )
745
816
 
746
817
  # Now map from vacuum coordinates to element map coordinates
747
818
  # This depends on how the element map was created
748
- if hasattr(self.shared, 'element_map_crop') and self.shared.element_map_crop:
819
+ if (
820
+ hasattr(self.shared, "element_map_crop")
821
+ and self.shared.element_map_crop
822
+ ):
749
823
  crop_info = self.shared.element_map_crop
750
824
 
751
825
  # Adjust for cropping
752
- if 'min_x' in crop_info and 'min_y' in crop_info:
753
- elem_x = int(vac_x - crop_info['min_x'])
754
- elem_y = int(vac_y - crop_info['min_y'])
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"])
755
829
 
756
830
  # Adjust for resizing
757
- if 'resize_factor' in crop_info and 'original_cropped_shape' in crop_info and 'resized_shape' in crop_info:
758
- orig_h, orig_w = crop_info['original_cropped_shape']
759
- resized_h, resized_w = crop_info['resized_shape']
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"]
760
838
 
761
839
  # Scale to resized coordinates
762
840
  elem_x = int(elem_x * resized_w / orig_w)
763
841
  elem_y = int(elem_y * resized_h / orig_h)
764
842
 
765
- LOGGER.debug(f"Mapped vacuum ({vac_x:.1f}, {vac_y:.1f}) to element map ({elem_x}, {elem_y})")
843
+ LOGGER.debug(
844
+ f"Mapped vacuum ({vac_x:.1f}, {vac_y:.1f}) to element map ({elem_x}, {elem_y})"
845
+ )
766
846
 
767
847
  # Check bounds and return element
768
848
  height, width = self.element_map.shape
@@ -782,19 +862,23 @@ class ElementMapGenerator:
782
862
  The name of the element (e.g., 'FLOOR', 'WALL', 'ROOM_1', etc.)
783
863
  """
784
864
  if element_code is None:
785
- return 'NONE'
865
+ return "NONE"
786
866
 
787
867
  # Check if it's a room
788
868
  if element_code >= 100:
789
869
  room_number = element_code - 100
790
- return f'ROOM_{room_number}'
870
+ return f"ROOM_{room_number}"
791
871
 
792
872
  # Check standard elements
793
873
  for name, code in vars(DrawableElement).items():
794
- if not name.startswith('_') and isinstance(code, int) and code == element_code:
874
+ if (
875
+ not name.startswith("_")
876
+ and isinstance(code, int)
877
+ and code == element_code
878
+ ):
795
879
  return name
796
880
 
797
- return f'UNKNOWN_{element_code}'
881
+ return f"UNKNOWN_{element_code}"
798
882
 
799
883
  def get_element_at_position(self, x: int, y: int, is_image_coords: bool = False):
800
884
  """Get the element code at the specified position in the element map.
@@ -814,7 +898,11 @@ class ElementMapGenerator:
814
898
  # If coordinates are from the image, convert them to element map coordinates first
815
899
  if is_image_coords and self.shared:
816
900
  # Get image dimensions
817
- if hasattr(self.shared, 'image_size') and self.shared.image_size is not None and len(self.shared.image_size) >= 2:
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
+ ):
818
906
  image_width = self.shared.image_size[0]
819
907
  image_height = self.shared.image_size[1]
820
908
  else:
@@ -823,8 +911,14 @@ class ElementMapGenerator:
823
911
  image_height = 1824
824
912
 
825
913
  # Get original element map dimensions (before resizing)
826
- if hasattr(self.shared, 'element_map_crop') and self.shared.element_map_crop is not None and 'original_cropped_shape' in self.shared.element_map_crop:
827
- original_map_height, original_map_width = self.shared.element_map_crop['original_cropped_shape']
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
+ ]
828
922
  else:
829
923
  # Estimate based on typical values
830
924
  original_map_width = 1310
@@ -842,22 +936,24 @@ class ElementMapGenerator:
842
936
  x = int((x + x_offset) * x_scale_to_map)
843
937
  y = int((y + y_offset) * y_scale_to_map)
844
938
 
845
- LOGGER.debug(f"Converted image coordinates ({x}, {y}) to element map coordinates")
939
+ LOGGER.debug(
940
+ f"Converted image coordinates ({x}, {y}) to element map coordinates"
941
+ )
846
942
 
847
943
  # Adjust coordinates if the element map has been cropped and resized
848
- if self.shared and hasattr(self.shared, 'element_map_crop'):
944
+ if self.shared and hasattr(self.shared, "element_map_crop"):
849
945
  # Get the crop information
850
946
  crop_info = self.shared.element_map_crop
851
947
 
852
948
  # Adjust coordinates to the cropped map
853
- x_cropped = x - crop_info['min_x']
854
- y_cropped = y - crop_info['min_y']
949
+ x_cropped = x - crop_info["min_x"]
950
+ y_cropped = y - crop_info["min_y"]
855
951
 
856
952
  # If the map has been resized, adjust coordinates further
857
- if 'resize_factor' in crop_info:
858
- resize_factor = crop_info['resize_factor']
859
- original_cropped_shape = crop_info.get('original_cropped_shape')
860
- resized_shape = crop_info.get('resized_shape')
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")
861
957
 
862
958
  if original_cropped_shape and resized_shape:
863
959
  # Calculate scaling factors