maps4fs 2.9.2__py3-none-any.whl → 2.9.37__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.
@@ -542,6 +542,17 @@ class GRLE(ImageComponent, XMLComponent):
542
542
 
543
543
  for layer in texture_component.get_area_type_layers():
544
544
  pixel_value = area_type_to_pixel_value(layer.area_type) # type: ignore
545
+ # * Not enabled for now.
546
+ # * If the layer is invisible, we need to draw the mask from the info layer.
547
+ # if layer.invisible:
548
+ # self.logger.debug("Processing invisible area type layer: %s.", layer.name)
549
+ # if layer.info_layer:
550
+ # self.logger.debug("Info layer available: %s.", layer.info_layer)
551
+ # weight_image = self.draw_invisible_layer_mask(layer, environment_size)
552
+ # else:
553
+ # self.logger.debug("No info layer available for layer: %s.", layer.name)
554
+ # continue
555
+ # else:
545
556
  weight_image = self.get_resized_weight(layer, environment_size) # type: ignore
546
557
  if weight_image is None:
547
558
  self.logger.warning("Weight image for area type layer not found in %s.", layer.name)
@@ -562,6 +573,52 @@ class GRLE(ImageComponent, XMLComponent):
562
573
  self.logger.debug("Environment InfoLayer PNG file saved: %s.", info_layer_environment_path)
563
574
  self.preview_paths["environment"] = info_layer_environment_path
564
575
 
576
+ # def draw_invisible_layer_mask(self, layer: Layer, resize_to: int) -> np.ndarray:
577
+ # """Draw the mask for the invisible layer.
578
+
579
+ # Arguments:
580
+ # layer (Layer): The layer for which to draw the mask.
581
+ # resize_to (int): The size to which the mask should be resized.
582
+
583
+ # Returns:
584
+ # np.ndarray: The resized mask.
585
+ # """
586
+ # mask = np.zeros((self.map.size, self.map.size), dtype=np.uint8)
587
+ # polygons = self.get_infolayer_data(Parameters.TEXTURES, layer.info_layer)
588
+ # self.logger.debug("Found %d polygons in info layer %s.", len(polygons), layer.info_layer)
589
+
590
+ # for polygon in polygons:
591
+ # try:
592
+ # fitted_polygon = self.fit_object_into_bounds(
593
+ # polygon_points=polygon,
594
+ # # margin=self.map.grle_settings.farmland_margin,
595
+ # angle=self.rotation,
596
+ # )
597
+ # except ValueError as e:
598
+ # self.logger.debug(
599
+ # "Polygon could not be fitted into the map bounds with error: %s",
600
+ # e,
601
+ # )
602
+ # continue
603
+ # polygon_np = self.polygon_points_to_np(fitted_polygon)
604
+
605
+ # try:
606
+ # cv2.fillPoly(mask, [polygon_np], (float(255),)) # type: ignore
607
+ # except Exception as e:
608
+ # self.logger.debug(
609
+ # "Polygon could not be added to the mask with error: %s",
610
+ # e,
611
+ # )
612
+ # continue
613
+
614
+ # resized_mask = cv2.resize(
615
+ # mask,
616
+ # (resize_to, resize_to),
617
+ # interpolation=cv2.INTER_NEAREST,
618
+ # )
619
+
620
+ # return resized_mask
621
+
565
622
  @monitor_performance
566
623
  def get_resized_weight(
567
624
  self, layer: Layer, resize_to: int, dilations: int = 3
@@ -12,13 +12,14 @@ import cv2
12
12
  import numpy as np
13
13
  from tqdm import tqdm
14
14
 
15
+ from maps4fs.generator.component.base.component_image import ImageComponent
15
16
  from maps4fs.generator.component.base.component_xml import XMLComponent
16
17
  from maps4fs.generator.monitor import monitor_performance
17
18
  from maps4fs.generator.settings import Parameters
18
19
 
19
20
  NODE_ID_STARTING_VALUE = 2000
20
21
  SPLINES_NODE_ID_STARTING_VALUE = 5000
21
- TREE_NODE_ID_STARTING_VALUE = 10000
22
+ TREE_NODE_ID_STARTING_VALUE = 30000
22
23
 
23
24
  FIELDS_ATTRIBUTES = [
24
25
  ("angle", "integer", "0"),
@@ -30,7 +31,7 @@ FIELDS_ATTRIBUTES = [
30
31
  ]
31
32
 
32
33
 
33
- class I3d(XMLComponent):
34
+ class I3d(XMLComponent, ImageComponent):
34
35
  """Component for map i3d file settings and configuration.
35
36
 
36
37
  Arguments:
@@ -690,9 +691,13 @@ class I3d(XMLComponent):
690
691
 
691
692
  return recommended_step if not current_step else max(recommended_step, current_step)
692
693
 
693
- def get_not_resized_dem(self) -> np.ndarray | None:
694
+ def get_not_resized_dem(self, with_foundations: bool = False) -> np.ndarray | None:
694
695
  """Reads the not resized DEM image from the background component.
695
696
 
697
+ Arguments:
698
+ with_foundations (bool, optional): Whether to get the DEM with foundations.
699
+ Defaults to False.
700
+
696
701
  Returns:
697
702
  np.ndarray | None: The not resized DEM image or None if the image could not be read.
698
703
  """
@@ -701,11 +706,60 @@ class I3d(XMLComponent):
701
706
  self.logger.warning("Background component not found.")
702
707
  return None
703
708
 
704
- if not background_component.not_resized_path:
709
+ dem_path = (
710
+ background_component.not_resized_with_foundations_path
711
+ if with_foundations
712
+ else background_component.not_resized_path
713
+ )
714
+
715
+ if not dem_path or not os.path.isfile(dem_path):
705
716
  self.logger.warning("Not resized DEM path not found.")
706
717
  return None
707
718
 
708
- not_resized_dem = cv2.imread(background_component.not_resized_path, cv2.IMREAD_UNCHANGED)
719
+ not_resized_dem = cv2.imread(dem_path, cv2.IMREAD_UNCHANGED)
720
+
721
+ return not_resized_dem
722
+
723
+ def get_not_resized_dem_with_foundations(
724
+ self, allow_fallback: bool = False
725
+ ) -> np.ndarray | None:
726
+ """Gets the not resized DEM with foundations. If the DEM with foundations is not found
727
+ and allow_fallback is True, the method returns the not resized DEM without foundations.
728
+
729
+ Arguments:
730
+ allow_fallback (bool, optional): Whether to allow fallback to DEM without
731
+ foundations. Defaults to False.
732
+
733
+ Returns:
734
+ np.ndarray | None: The not resized DEM image or None if the image could not be read.
735
+ """
736
+ dem_with_foundations = self.get_not_resized_dem(with_foundations=True)
737
+
738
+ if dem_with_foundations is not None:
739
+ return dem_with_foundations
740
+ self.logger.warning("Not resized DEM with foundations not found.")
741
+ if allow_fallback:
742
+ return self.get_not_resized_dem(with_foundations=False)
743
+ return None
744
+
745
+ def get_not_resized_dem_with_flattened_roads(self) -> np.ndarray | None:
746
+ """Gets the not resized DEM with flattened roads.
747
+
748
+ Returns:
749
+ np.ndarray | None: The not resized DEM image or None if the image could not be read.
750
+ """
751
+ background_component = self.map.get_background_component()
752
+ if not background_component:
753
+ self.logger.warning("Background component not found.")
754
+ return None
755
+
756
+ dem_path = background_component.not_resized_with_flattened_roads_path
757
+
758
+ if not dem_path or not os.path.isfile(dem_path):
759
+ self.logger.warning("Not resized DEM with flattened roads path not found.")
760
+ return None
761
+
762
+ not_resized_dem = cv2.imread(dem_path, cv2.IMREAD_UNCHANGED)
709
763
 
710
764
  return not_resized_dem
711
765
 
@@ -21,6 +21,16 @@ class Layer:
21
21
  usage (str | None): Usage of the layer.
22
22
  background (bool): Flag to determine if the layer is a background.
23
23
  invisible (bool): Flag to determine if the layer is invisible.
24
+ procedural (list[str] | None): List of procedural textures to apply.
25
+ border (int | None): Border size in pixels.
26
+ precise_tags (dict[str, str | list[str]] | None): Dictionary of precise tags to search for.
27
+ precise_usage (str | None): Precise usage of the layer.
28
+ area_type (str | None): Type of the area (e.g., residential, commercial).
29
+ area_water (bool): Flag to determine if the area is water.
30
+ indoor (bool): Flag to determine if the layer is indoor.
31
+ merge_into (str | None): Name of the layer to merge into.
32
+ building_category (str | None): Category of the building.
33
+ external (bool): External layers not being used by the game directly.
24
34
 
25
35
  Attributes:
26
36
  name (str): Name of the layer.
@@ -50,6 +60,9 @@ class Layer:
50
60
  area_water: bool = False,
51
61
  indoor: bool = False,
52
62
  merge_into: str | None = None,
63
+ building_category: str | None = None,
64
+ external: bool = False,
65
+ road_texture: str | None = None,
53
66
  ):
54
67
  self.name = name
55
68
  self.count = count
@@ -70,6 +83,9 @@ class Layer:
70
83
  self.area_water = area_water
71
84
  self.indoor = indoor
72
85
  self.merge_into = merge_into
86
+ self.building_category = building_category
87
+ self.external = external
88
+ self.road_texture = road_texture
73
89
 
74
90
  def to_json(self) -> dict[str, str | list[str] | bool]: # type: ignore
75
91
  """Returns dictionary with layer data.
@@ -96,6 +112,9 @@ class Layer:
96
112
  "area_water": self.area_water,
97
113
  "indoor": self.indoor,
98
114
  "merge_into": self.merge_into,
115
+ "building_category": self.building_category,
116
+ "external": self.external,
117
+ "road_texture": self.road_texture,
99
118
  }
100
119
 
101
120
  data = {k: v for k, v in data.items() if v is not None}