maps4fs 2.1.1__py3-none-any.whl → 2.1.3__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.
- maps4fs/generator/component/background.py +97 -0
- maps4fs/generator/component/base/component_image.py +56 -0
- maps4fs/generator/component/grle.py +178 -0
- maps4fs/generator/component/layer.py +9 -0
- maps4fs/generator/component/texture.py +30 -0
- maps4fs/generator/game.py +32 -0
- maps4fs/generator/settings.py +2 -0
- {maps4fs-2.1.1.dist-info → maps4fs-2.1.3.dist-info}/METADATA +4 -1
- {maps4fs-2.1.1.dist-info → maps4fs-2.1.3.dist-info}/RECORD +12 -12
- {maps4fs-2.1.1.dist-info → maps4fs-2.1.3.dist-info}/WHEEL +0 -0
- {maps4fs-2.1.1.dist-info → maps4fs-2.1.3.dist-info}/licenses/LICENSE.md +0 -0
- {maps4fs-2.1.1.dist-info → maps4fs-2.1.3.dist-info}/top_level.txt +0 -0
@@ -21,6 +21,8 @@ from maps4fs.generator.component.dem import DEM
|
|
21
21
|
from maps4fs.generator.component.texture import Texture
|
22
22
|
from maps4fs.generator.settings import Parameters
|
23
23
|
|
24
|
+
SEGMENT_LENGTH = 2
|
25
|
+
|
24
26
|
|
25
27
|
class Background(MeshComponent, ImageComponent):
|
26
28
|
"""Component for creating 3D obj files based on DEM data around the map.
|
@@ -97,6 +99,10 @@ class Background(MeshComponent, ImageComponent):
|
|
97
99
|
cutted_dem_path = self.save_map_dem(self.output_path)
|
98
100
|
shutil.copyfile(self.output_path, self.not_substracted_path)
|
99
101
|
self.save_map_dem(self.output_path, save_path=self.not_resized_path)
|
102
|
+
|
103
|
+
if self.map.background_settings.flatten_roads:
|
104
|
+
self.flatten_roads()
|
105
|
+
|
100
106
|
if self.game.additional_dem_name is not None:
|
101
107
|
self.make_copy(cutted_dem_path, self.game.additional_dem_name)
|
102
108
|
|
@@ -723,3 +729,94 @@ class Background(MeshComponent, ImageComponent):
|
|
723
729
|
self.assets.water_mesh = elevated_save_path
|
724
730
|
|
725
731
|
self.plane_from_np(elevated_water, elevated_save_path, include_zeros=False)
|
732
|
+
|
733
|
+
def flatten_roads(self) -> None:
|
734
|
+
"""Flattens the roads in the DEM data by averaging the height values along the road polylines."""
|
735
|
+
if not self.not_resized_path or not os.path.isfile(self.not_resized_path):
|
736
|
+
self.logger.warning("No DEM data found for flattening roads.")
|
737
|
+
return
|
738
|
+
|
739
|
+
dem_image = cv2.imread(self.not_resized_path, cv2.IMREAD_UNCHANGED)
|
740
|
+
if dem_image is None:
|
741
|
+
self.logger.warning("Failed to read DEM data.")
|
742
|
+
return
|
743
|
+
|
744
|
+
roads_polylines = self.get_infolayer_data(Parameters.TEXTURES, Parameters.ROADS_POLYLINES)
|
745
|
+
if not roads_polylines:
|
746
|
+
self.logger.warning("No roads polylines found in textures info layer.")
|
747
|
+
return
|
748
|
+
|
749
|
+
self.logger.debug("Found %s roads polylines in textures info layer.", len(roads_polylines))
|
750
|
+
|
751
|
+
full_mask = np.zeros(dem_image.shape, dtype=np.uint8)
|
752
|
+
|
753
|
+
for road_polyline in tqdm(roads_polylines, desc="Flattening roads", unit="road"):
|
754
|
+
points = road_polyline.get("points")
|
755
|
+
width = road_polyline.get("width")
|
756
|
+
if not points or not width:
|
757
|
+
self.logger.warning("Skipping road with insufficient data: %s", road_polyline)
|
758
|
+
continue
|
759
|
+
|
760
|
+
try:
|
761
|
+
fitted_road = self.fit_object_into_bounds(
|
762
|
+
linestring_points=points, angle=self.rotation
|
763
|
+
)
|
764
|
+
except ValueError as e:
|
765
|
+
self.logger.debug(
|
766
|
+
"Road polyline could not be fitted into the map bounds with error: %s",
|
767
|
+
e,
|
768
|
+
)
|
769
|
+
continue
|
770
|
+
|
771
|
+
polyline = shapely.LineString(fitted_road)
|
772
|
+
|
773
|
+
total_length = polyline.length
|
774
|
+
self.logger.debug("Total length of the road polyline: %s", total_length)
|
775
|
+
|
776
|
+
current_distance = 0
|
777
|
+
segments: list[shapely.LineString] = []
|
778
|
+
while current_distance < total_length:
|
779
|
+
start_point = polyline.interpolate(current_distance)
|
780
|
+
end_distance = min(current_distance + SEGMENT_LENGTH, total_length)
|
781
|
+
end_point = polyline.interpolate(end_distance)
|
782
|
+
|
783
|
+
# Create a small segment LineString
|
784
|
+
segment = shapely.LineString([start_point, end_point])
|
785
|
+
segments.append(segment)
|
786
|
+
current_distance += SEGMENT_LENGTH
|
787
|
+
|
788
|
+
self.logger.debug("Number of segments created: %s", len(segments))
|
789
|
+
|
790
|
+
road_polygons: list[shapely.Polygon] = []
|
791
|
+
for segment in segments:
|
792
|
+
polygon = segment.buffer(
|
793
|
+
width * 2, resolution=4, cap_style="flat", join_style="mitre"
|
794
|
+
)
|
795
|
+
road_polygons.append(polygon)
|
796
|
+
|
797
|
+
for polygon in road_polygons:
|
798
|
+
polygon_points = polygon.exterior.coords
|
799
|
+
road_np = self.polygon_points_to_np(polygon_points)
|
800
|
+
mask = np.zeros(dem_image.shape, dtype=np.uint8)
|
801
|
+
|
802
|
+
try:
|
803
|
+
cv2.fillPoly(mask, [road_np], 255) # type: ignore
|
804
|
+
except Exception as e:
|
805
|
+
self.logger.debug("Could not create mask for road with error: %s", e)
|
806
|
+
continue
|
807
|
+
|
808
|
+
mean_value = cv2.mean(dem_image, mask=mask)[0] # type: ignore
|
809
|
+
dem_image[mask == 255] = mean_value
|
810
|
+
full_mask[mask == 255] = 255
|
811
|
+
|
812
|
+
main_dem_path = self.game.dem_file_path(self.map_directory)
|
813
|
+
dem_image = self.blur_by_mask(dem_image, full_mask)
|
814
|
+
dem_image = self.blur_edges_by_mask(dem_image, full_mask)
|
815
|
+
|
816
|
+
output_size = dem_image.shape[0] + 1
|
817
|
+
resized_dem = cv2.resize(
|
818
|
+
dem_image, (output_size, output_size), interpolation=cv2.INTER_NEAREST
|
819
|
+
)
|
820
|
+
|
821
|
+
cv2.imwrite(main_dem_path, resized_dem)
|
822
|
+
self.logger.debug("Flattened roads saved to DEM file: %s", main_dem_path)
|
@@ -175,3 +175,59 @@ class ImageComponent(Component):
|
|
175
175
|
elif blur_radius % 2 == 0:
|
176
176
|
blur_radius += 1
|
177
177
|
return blur_radius
|
178
|
+
|
179
|
+
def blur_by_mask(self, data: np.ndarray, mask: np.ndarray, blur_radius: int = 3) -> np.ndarray:
|
180
|
+
"""Blurs the provided image only where the mask is set.
|
181
|
+
|
182
|
+
Arguments:
|
183
|
+
data (np.ndarray): The image data to be blurred.
|
184
|
+
mask (np.ndarray): The mask where the blur should be applied.
|
185
|
+
|
186
|
+
Returns:
|
187
|
+
np.ndarray: The image with the blur applied according to the mask.
|
188
|
+
"""
|
189
|
+
if data.shape[:2] != mask.shape[:2]:
|
190
|
+
raise ValueError("Data and mask must have the same dimensions.")
|
191
|
+
|
192
|
+
# Create a blurred version of the data
|
193
|
+
blurred_data = cv2.GaussianBlur(data, (blur_radius, blur_radius), sigmaX=3)
|
194
|
+
|
195
|
+
# Combine the blurred data with the original data using the mask
|
196
|
+
result = np.where(mask == 255, blurred_data, data)
|
197
|
+
|
198
|
+
return result
|
199
|
+
|
200
|
+
def blur_edges_by_mask(
|
201
|
+
self,
|
202
|
+
data: np.ndarray,
|
203
|
+
mask: np.ndarray,
|
204
|
+
bigger_kernel: int = 3,
|
205
|
+
smaller_kernel: int = 1,
|
206
|
+
iterations: int = 1,
|
207
|
+
) -> np.ndarray:
|
208
|
+
"""Blurs the edges of the edge region where changes were made by mask earlier.
|
209
|
+
Creates a slightly bigger mask, a slightly smaller mask, subtract them
|
210
|
+
and obtains the mask for the edges.
|
211
|
+
Then applies blur to the image data only where the mask is set.
|
212
|
+
|
213
|
+
Arguments:
|
214
|
+
data (np.ndarray): The image data to be blurred.
|
215
|
+
mask (np.ndarray): The mask where changes were made.
|
216
|
+
blur_radius (int): The radius of the blur.
|
217
|
+
|
218
|
+
Returns:
|
219
|
+
np.ndarray: The image with the edges blurred according to the mask.
|
220
|
+
"""
|
221
|
+
if data.shape[:2] != mask.shape[:2]:
|
222
|
+
raise ValueError("Data and mask must have the same dimensions.")
|
223
|
+
|
224
|
+
bigger_mask = cv2.dilate(
|
225
|
+
mask, np.ones((bigger_kernel, bigger_kernel), np.uint8), iterations=iterations
|
226
|
+
)
|
227
|
+
smaller_mask = cv2.erode(
|
228
|
+
mask, np.ones((smaller_kernel, smaller_kernel), np.uint8), iterations=iterations
|
229
|
+
)
|
230
|
+
|
231
|
+
edge_mask = cv2.subtract(bigger_mask, smaller_mask)
|
232
|
+
|
233
|
+
return self.blur_by_mask(data, edge_mask)
|
@@ -11,8 +11,13 @@ from tqdm import tqdm
|
|
11
11
|
|
12
12
|
from maps4fs.generator.component.base.component_image import ImageComponent
|
13
13
|
from maps4fs.generator.component.base.component_xml import XMLComponent
|
14
|
+
from maps4fs.generator.component.layer import Layer
|
14
15
|
from maps4fs.generator.settings import Parameters
|
15
16
|
|
17
|
+
# This value sums up the pixel value of the basic area type to convert it from "No Water" to "Near Water".
|
18
|
+
# For example, if the basic area type is "city" (1), then the pixel value for "near water" will be 9.
|
19
|
+
WATER_AREA_PIXEL_VALUE = 8
|
20
|
+
|
16
21
|
|
17
22
|
def plant_to_pixel_value(plant_name: str) -> int | None:
|
18
23
|
"""Returns the pixel value representation of the plant.
|
@@ -31,6 +36,27 @@ def plant_to_pixel_value(plant_name: str) -> int | None:
|
|
31
36
|
return plants.get(plant_name)
|
32
37
|
|
33
38
|
|
39
|
+
def area_type_to_pixel_value(area_type: str) -> int | None:
|
40
|
+
"""Returns the pixel value representation of the area type.
|
41
|
+
If not found, returns None.
|
42
|
+
|
43
|
+
Arguments:
|
44
|
+
area_type (str): name of the area type
|
45
|
+
|
46
|
+
Returns:
|
47
|
+
int | None: pixel value of the area type or None if not found.
|
48
|
+
"""
|
49
|
+
area_types = {
|
50
|
+
"open_land": 0,
|
51
|
+
"city": 1,
|
52
|
+
"village": 2,
|
53
|
+
"harbor": 3,
|
54
|
+
"industrial": 4,
|
55
|
+
"open_water": 5,
|
56
|
+
}
|
57
|
+
return area_types.get(area_type)
|
58
|
+
|
59
|
+
|
34
60
|
class GRLE(ImageComponent, XMLComponent):
|
35
61
|
"""Component for to generate InfoLayer PNG files based on GRLE schema.
|
36
62
|
|
@@ -104,6 +130,9 @@ class GRLE(ImageComponent, XMLComponent):
|
|
104
130
|
self._add_farmlands()
|
105
131
|
if self.game.plants_processing and self.map.grle_settings.add_grass:
|
106
132
|
self._add_plants()
|
133
|
+
if self.game.environment_processing:
|
134
|
+
self._process_environment()
|
135
|
+
self._process_indoor()
|
107
136
|
|
108
137
|
def previews(self) -> list[str]:
|
109
138
|
"""Returns a list of paths to the preview images (empty list).
|
@@ -130,6 +159,9 @@ class GRLE(ImageComponent, XMLComponent):
|
|
130
159
|
cv2.imwrite(save_path, image_colored)
|
131
160
|
preview_paths.append(save_path)
|
132
161
|
|
162
|
+
if preview_name != "farmlands":
|
163
|
+
continue
|
164
|
+
|
133
165
|
with_fields_save_path = os.path.join(
|
134
166
|
self.previews_directory, f"{preview_name}_with_fields.png"
|
135
167
|
)
|
@@ -462,3 +494,149 @@ class GRLE(ImageComponent, XMLComponent):
|
|
462
494
|
image_np[:, 0] = 0 # Left side
|
463
495
|
image_np[:, -1] = 0 # Right side
|
464
496
|
return image_np
|
497
|
+
|
498
|
+
def _process_environment(self) -> None:
|
499
|
+
info_layer_environment_path = self.game.get_environment_path(self.map_directory)
|
500
|
+
if not info_layer_environment_path or not os.path.isfile(info_layer_environment_path):
|
501
|
+
self.logger.warning(
|
502
|
+
"Environment InfoLayer PNG file not found in %s.", info_layer_environment_path
|
503
|
+
)
|
504
|
+
return
|
505
|
+
|
506
|
+
self.logger.debug(
|
507
|
+
"Processing environment InfoLayer PNG file: %s.", info_layer_environment_path
|
508
|
+
)
|
509
|
+
|
510
|
+
environment_image = cv2.imread(info_layer_environment_path, cv2.IMREAD_UNCHANGED)
|
511
|
+
if environment_image is None:
|
512
|
+
self.logger.error("Failed to read the environment InfoLayer PNG file.")
|
513
|
+
return
|
514
|
+
|
515
|
+
self.logger.debug(
|
516
|
+
"Environment InfoLayer PNG file loaded, shape: %s.", environment_image.shape
|
517
|
+
)
|
518
|
+
|
519
|
+
environment_size = int(environment_image.shape[0])
|
520
|
+
|
521
|
+
# 1. Get the texture layers that contain "area_type" property.
|
522
|
+
# 2. Read the corresponding weight image (not the dissolved ones!).
|
523
|
+
# 3. Resize it to match the environment image size (probably 1/4 of the texture size).
|
524
|
+
# 4. Dilate the texture mask to make it little bit bigger.
|
525
|
+
# 5. Set the corresponding pixel values of the environment image to the pixel value of the area type.
|
526
|
+
# 6. Get the texture layer that "area_water" is True.
|
527
|
+
# 7. Same as resize, dilate, etc.
|
528
|
+
# 8. Sum the current pixel value with the WATER_AREA_PIXEL_VALUE.
|
529
|
+
|
530
|
+
texture_component = self.map.get_texture_component()
|
531
|
+
if not texture_component:
|
532
|
+
self.logger.warning("Texture component not found in the map.")
|
533
|
+
return
|
534
|
+
|
535
|
+
for layer in texture_component.get_area_type_layers():
|
536
|
+
pixel_value = area_type_to_pixel_value(layer.area_type) # type: ignore
|
537
|
+
weight_image = self.get_resized_weight(layer, environment_size) # type: ignore
|
538
|
+
if weight_image is None:
|
539
|
+
self.logger.warning("Weight image for area type layer not found in %s.", layer.name)
|
540
|
+
continue
|
541
|
+
environment_image[weight_image > 0] = pixel_value # type: ignore
|
542
|
+
|
543
|
+
for layer in texture_component.get_water_area_layers():
|
544
|
+
pixel_value = WATER_AREA_PIXEL_VALUE
|
545
|
+
weight_image = self.get_resized_weight(layer, environment_size)
|
546
|
+
if weight_image is None:
|
547
|
+
self.logger.warning(
|
548
|
+
"Weight image for water area layer not found in %s.", layer.name
|
549
|
+
)
|
550
|
+
continue
|
551
|
+
environment_image[weight_image > 0] += pixel_value # type: ignore
|
552
|
+
|
553
|
+
cv2.imwrite(info_layer_environment_path, environment_image)
|
554
|
+
self.logger.debug("Environment InfoLayer PNG file saved: %s.", info_layer_environment_path)
|
555
|
+
self.preview_paths["environment"] = info_layer_environment_path
|
556
|
+
|
557
|
+
def get_resized_weight(
|
558
|
+
self, layer: Layer, resize_to: int, dilations: int = 3
|
559
|
+
) -> np.ndarray | None:
|
560
|
+
"""Get the resized weight image for a given layer.
|
561
|
+
|
562
|
+
Arguments:
|
563
|
+
layer (Layer): The layer for which to get the weight image.
|
564
|
+
resize_to (int): The size to which the weight image should be resized.
|
565
|
+
dilations (int): The number of dilations to apply to the weight image.
|
566
|
+
|
567
|
+
Returns:
|
568
|
+
np.ndarray | None: The resized and dilated weight image, or None if the image could not be loaded.
|
569
|
+
"""
|
570
|
+
weight_image_path = layer.get_preview_or_path(
|
571
|
+
self.game.weights_dir_path(self.map_directory)
|
572
|
+
)
|
573
|
+
self.logger.debug("Weight image path for area type layer: %s.", weight_image_path)
|
574
|
+
|
575
|
+
if not weight_image_path or not os.path.isfile(weight_image_path):
|
576
|
+
self.logger.warning(
|
577
|
+
"Weight image for area type layer not found in %s.", weight_image_path
|
578
|
+
)
|
579
|
+
return None
|
580
|
+
|
581
|
+
weight_image = cv2.imread(weight_image_path, cv2.IMREAD_UNCHANGED)
|
582
|
+
if weight_image is None:
|
583
|
+
self.logger.error("Failed to read the weight image for area type layer.")
|
584
|
+
return None
|
585
|
+
|
586
|
+
self.logger.debug("Weight image for area type layer loaded, shape: %s.", weight_image.shape)
|
587
|
+
|
588
|
+
# Resize the weight image to match the environment image size.
|
589
|
+
weight_image = cv2.resize(
|
590
|
+
weight_image,
|
591
|
+
(resize_to, resize_to),
|
592
|
+
interpolation=cv2.INTER_NEAREST,
|
593
|
+
)
|
594
|
+
|
595
|
+
self.logger.debug(
|
596
|
+
"Resized weight image for area type layer, new shape: %s.", weight_image.shape
|
597
|
+
)
|
598
|
+
|
599
|
+
if dilations <= 0:
|
600
|
+
return weight_image
|
601
|
+
|
602
|
+
dilated_weight_image = cv2.dilate(
|
603
|
+
weight_image.astype(np.uint8), np.ones((dilations, dilations), np.uint8), iterations=dilations # type: ignore
|
604
|
+
)
|
605
|
+
|
606
|
+
return dilated_weight_image
|
607
|
+
|
608
|
+
def _process_indoor(self) -> None:
|
609
|
+
"""Processes the indoor layers."""
|
610
|
+
info_layer_indoor_path = self.game.get_indoor_mask_path(self.map_directory)
|
611
|
+
if not info_layer_indoor_path or not os.path.isfile(info_layer_indoor_path):
|
612
|
+
self.logger.warning(
|
613
|
+
"Indoor InfoLayer PNG file not found in %s.", info_layer_indoor_path
|
614
|
+
)
|
615
|
+
return
|
616
|
+
|
617
|
+
indoor_mask_image = cv2.imread(info_layer_indoor_path, cv2.IMREAD_UNCHANGED)
|
618
|
+
if indoor_mask_image is None:
|
619
|
+
self.logger.warning(
|
620
|
+
"Failed to read the indoor InfoLayer PNG file %s.", info_layer_indoor_path
|
621
|
+
)
|
622
|
+
return
|
623
|
+
|
624
|
+
indoor_mask_size = int(indoor_mask_image.shape[0])
|
625
|
+
self.logger.debug("Indoor InfoLayer PNG file loaded, shape: %s.", indoor_mask_image.shape)
|
626
|
+
|
627
|
+
texture_component = self.map.get_texture_component()
|
628
|
+
if not texture_component:
|
629
|
+
self.logger.warning("Texture component not found in the map.")
|
630
|
+
return
|
631
|
+
|
632
|
+
for layer in texture_component.get_indoor_layers():
|
633
|
+
weight_image = self.get_resized_weight(layer, indoor_mask_size, dilations=0)
|
634
|
+
if weight_image is None:
|
635
|
+
self.logger.warning("Weight image for indoor layer not found in %s.", layer.name)
|
636
|
+
continue
|
637
|
+
|
638
|
+
indoor_mask_image[weight_image > 0] = 1
|
639
|
+
|
640
|
+
cv2.imwrite(info_layer_indoor_path, indoor_mask_image)
|
641
|
+
self.logger.debug("Indoor InfoLayer PNG file saved: %s.", info_layer_indoor_path)
|
642
|
+
self.preview_paths["indoor"] = info_layer_indoor_path
|
@@ -46,6 +46,9 @@ class Layer:
|
|
46
46
|
border: int | None = None,
|
47
47
|
precise_tags: dict[str, str | list[str] | bool] | None = None,
|
48
48
|
precise_usage: str | None = None,
|
49
|
+
area_type: str | None = None,
|
50
|
+
area_water: bool = False,
|
51
|
+
indoor: bool = False,
|
49
52
|
):
|
50
53
|
self.name = name
|
51
54
|
self.count = count
|
@@ -62,6 +65,9 @@ class Layer:
|
|
62
65
|
self.border = border
|
63
66
|
self.precise_tags = precise_tags
|
64
67
|
self.precise_usage = precise_usage
|
68
|
+
self.area_type = area_type
|
69
|
+
self.area_water = area_water
|
70
|
+
self.indoor = indoor
|
65
71
|
|
66
72
|
def to_json(self) -> dict[str, str | list[str] | bool]: # type: ignore
|
67
73
|
"""Returns dictionary with layer data.
|
@@ -84,6 +90,9 @@ class Layer:
|
|
84
90
|
"border": self.border,
|
85
91
|
"precise_tags": self.precise_tags,
|
86
92
|
"precise_usage": self.precise_usage,
|
93
|
+
"area_type": self.area_type,
|
94
|
+
"area_water": self.area_water,
|
95
|
+
"indoor": self.indoor,
|
87
96
|
}
|
88
97
|
|
89
98
|
data = {k: v for k, v in data.items() if v is not None}
|
@@ -137,6 +137,36 @@ class Texture(ImageComponent):
|
|
137
137
|
"""
|
138
138
|
return [layer for layer in self.layers if layer.usage == usage]
|
139
139
|
|
140
|
+
def get_area_type_layers(self, area_type: str | None = None) -> list[Layer]:
|
141
|
+
"""Returns layers by area type. If area_type is None, returns all layers
|
142
|
+
with area_type set (not None).
|
143
|
+
|
144
|
+
Arguments:
|
145
|
+
area_type (str | None): Area type of the layer.
|
146
|
+
|
147
|
+
Returns:
|
148
|
+
list[Layer]: List of layers.
|
149
|
+
"""
|
150
|
+
if area_type is None:
|
151
|
+
return [layer for layer in self.layers if layer.area_type is not None]
|
152
|
+
return [layer for layer in self.layers if layer.area_type == area_type]
|
153
|
+
|
154
|
+
def get_water_area_layers(self) -> list[Layer]:
|
155
|
+
"""Returns layers which are water areas.
|
156
|
+
|
157
|
+
Returns:
|
158
|
+
list[Layer]: List of layers which are water areas.
|
159
|
+
"""
|
160
|
+
return [layer for layer in self.layers if layer.area_water]
|
161
|
+
|
162
|
+
def get_indoor_layers(self) -> list[Layer]:
|
163
|
+
"""Returns layers which are indoor areas.
|
164
|
+
|
165
|
+
Returns:
|
166
|
+
list[Layer]: List of layers which are indoor areas.
|
167
|
+
"""
|
168
|
+
return [layer for layer in self.layers if layer.indoor]
|
169
|
+
|
140
170
|
def process(self) -> None:
|
141
171
|
"""Processes the data to generate textures."""
|
142
172
|
self._prepare_weights()
|
maps4fs/generator/game.py
CHANGED
@@ -40,6 +40,7 @@ class Game:
|
|
40
40
|
_tree_schema: str | None = None
|
41
41
|
_i3d_processing: bool = True
|
42
42
|
_plants_processing: bool = True
|
43
|
+
_environment_processing: bool = True
|
43
44
|
_fog_processing: bool = True
|
44
45
|
_dissolve: bool = True
|
45
46
|
|
@@ -180,6 +181,28 @@ class Game:
|
|
180
181
|
weights_dir = self.weights_dir_path(map_directory)
|
181
182
|
return os.path.join(weights_dir, "infoLayer_farmlands.png")
|
182
183
|
|
184
|
+
def get_environment_path(self, map_directory: str) -> str:
|
185
|
+
"""Returns the path to the environment file.
|
186
|
+
|
187
|
+
Arguments:
|
188
|
+
map_directory (str): The path to the map directory.
|
189
|
+
|
190
|
+
Returns:
|
191
|
+
str: The path to the environment file."""
|
192
|
+
weights_dir = self.weights_dir_path(map_directory)
|
193
|
+
return os.path.join(weights_dir, "infoLayer_environment.png")
|
194
|
+
|
195
|
+
def get_indoor_mask_path(self, map_directory: str) -> str:
|
196
|
+
"""Returns the path to the indoor mask file.
|
197
|
+
|
198
|
+
Arguments:
|
199
|
+
map_directory (str): The path to the map directory.
|
200
|
+
|
201
|
+
Returns:
|
202
|
+
str: The path to the indoor mask file."""
|
203
|
+
weights_dir = self.weights_dir_path(map_directory)
|
204
|
+
return os.path.join(weights_dir, "infoLayer_indoorMask.png")
|
205
|
+
|
183
206
|
def get_farmlands_xml_path(self, map_directory: str) -> str:
|
184
207
|
"""Returns the path to the farmlands xml file.
|
185
208
|
|
@@ -218,6 +241,14 @@ class Game:
|
|
218
241
|
bool: True if the i3d file should be processed, False otherwise."""
|
219
242
|
return self._i3d_processing
|
220
243
|
|
244
|
+
@property
|
245
|
+
def environment_processing(self) -> bool:
|
246
|
+
"""Returns whether the environment should be processed.
|
247
|
+
|
248
|
+
Returns:
|
249
|
+
bool: True if the environment should be processed, False otherwise."""
|
250
|
+
return self._environment_processing
|
251
|
+
|
221
252
|
@property
|
222
253
|
def fog_processing(self) -> bool:
|
223
254
|
"""Returns whether the fog should be processed.
|
@@ -269,6 +300,7 @@ class FS22(Game):
|
|
269
300
|
_map_template_path = os.path.join(working_directory, "data", "fs22-map-template.zip")
|
270
301
|
_texture_schema = os.path.join(working_directory, "data", "fs22-texture-schema.json")
|
271
302
|
_i3d_processing = False
|
303
|
+
_environment_processing = False
|
272
304
|
_fog_processing = False
|
273
305
|
_plants_processing = False
|
274
306
|
_dissolve = False
|
maps4fs/generator/settings.py
CHANGED
@@ -163,12 +163,14 @@ class BackgroundSettings(SettingsModel):
|
|
163
163
|
water_blurriness (int): blurriness of the water.
|
164
164
|
remove_center (bool): remove the center of the background terrain.
|
165
165
|
It will be used to remove the center of the map where the player starts.
|
166
|
+
flatten_roads (bool): if True, roads will be flattened in the DEM data.
|
166
167
|
"""
|
167
168
|
|
168
169
|
generate_background: bool = False
|
169
170
|
generate_water: bool = False
|
170
171
|
water_blurriness: int = 20
|
171
172
|
remove_center: bool = True
|
173
|
+
flatten_roads: bool = False
|
172
174
|
|
173
175
|
|
174
176
|
class GRLESettings(SettingsModel):
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: maps4fs
|
3
|
-
Version: 2.1.
|
3
|
+
Version: 2.1.3
|
4
4
|
Summary: Generate map templates for Farming Simulator from real places.
|
5
5
|
Author-email: iwatkot <iwatkot@gmail.com>
|
6
6
|
License: Apache License 2.0
|
@@ -546,6 +546,9 @@ Let's have a closer look at the fields:
|
|
546
546
|
- `border` - this value defines the border between the texture and the edge of the map. It's used to prevent the texture from being drawn on the edge of the map. The value is in pixels.
|
547
547
|
- `precise_tags` - can be used for more specific tags, for example instead of `"natural": "wood"` you can use `"leaf_type": "broadleaved"` to draw only broadleaved trees.
|
548
548
|
- `precise_usage` - the same as `usage`, but being used with `precise_tags`.
|
549
|
+
- `area_type` - one of the supported by Giants Editor area types, such as: "open_land", "city", "village", "harbor", "industrial", "open_water". It will be reflected in the environment info layer file.
|
550
|
+
- `area_water` - whenever this field is set to true, the area will be considered as water, and it will be changed in the environment info layer file.
|
551
|
+
- `indoor` - whenever this field is set to true, the area will be considered as indoor, and it will be reflected in the indoorMask info layer.
|
549
552
|
|
550
553
|
## Background terrain
|
551
554
|
|
@@ -1,27 +1,27 @@
|
|
1
1
|
maps4fs/__init__.py,sha256=Fy521EmVAWnhu6OvOInc97yrtJotFzcV0YfRB2b9O4s,314
|
2
2
|
maps4fs/logger.py,sha256=WDfR14hxqy8b6xtwL6YIu2LGzFO1sbt0LxMgfsDTOkA,865
|
3
3
|
maps4fs/generator/__init__.py,sha256=zZMLEkGzb4z0xql650gOtGSvcgX58DnJ2yN3vC2daRk,43
|
4
|
-
maps4fs/generator/game.py,sha256=
|
4
|
+
maps4fs/generator/game.py,sha256=Qqeh1rg0RFghhu9uhBMn_XKXYkTkg7fsjY1xO2eIZRw,13777
|
5
5
|
maps4fs/generator/map.py,sha256=x_NjUwKkDnh2cC61QpTfX88mFHAkQG1sxVwZckoV0mE,16357
|
6
6
|
maps4fs/generator/qgis.py,sha256=Es8hLuqN_KH8lDfnJE6He2rWYbAKJ3RGPn-o87S6CPI,6116
|
7
|
-
maps4fs/generator/settings.py,sha256=
|
7
|
+
maps4fs/generator/settings.py,sha256=bjEhn1918lxSsIRgmOXVpYWS3E4y72eqx2Ty7f99JKE,7788
|
8
8
|
maps4fs/generator/statistics.py,sha256=aynS3zbAtiwnU_YLKHPTiiaKW98_suvQUhy1SGBA6mc,2448
|
9
9
|
maps4fs/generator/component/__init__.py,sha256=s01yVVVi8R2xxNvflu2D6wTd9I_g73AMM2x7vAC7GX4,490
|
10
|
-
maps4fs/generator/component/background.py,sha256=
|
10
|
+
maps4fs/generator/component/background.py,sha256=0fTmghJZzmZMqleTmFc5C6A53eZm7nhMpLYmkELAHmQ,33507
|
11
11
|
maps4fs/generator/component/config.py,sha256=uL76h9UwyhZKZmbxz0mBmWtEPN6qYay4epTEqqtej60,8601
|
12
12
|
maps4fs/generator/component/dem.py,sha256=SH_2Zu5O4dhWtZeOkCwzDF4RU04XhTdpGFYaRYJkdjc,11905
|
13
|
-
maps4fs/generator/component/grle.py,sha256=
|
13
|
+
maps4fs/generator/component/grle.py,sha256=3BKGlR0q0t0NvmqeT81WieS6MIc_UlMQjIDDZiqTiws,27243
|
14
14
|
maps4fs/generator/component/i3d.py,sha256=L-QAbr3Z7Ye5N0BeS_qvY9bqYxYs0eVnRCGWp77etrE,26693
|
15
|
-
maps4fs/generator/component/layer.py,sha256
|
15
|
+
maps4fs/generator/component/layer.py,sha256=U_DzJTn1m_yGOtwuvbXxr7oL7YHHBGBcK37lyJNnZDk,6508
|
16
16
|
maps4fs/generator/component/satellite.py,sha256=OsxoNOCgkUtRzL7Geuqubsf6uoKXAIN8jQvrJ7IFeAI,4958
|
17
|
-
maps4fs/generator/component/texture.py,sha256=
|
17
|
+
maps4fs/generator/component/texture.py,sha256=bpHBwUksEbwPQrdQ8K94m_MIKXnWYnUMvP987JVabAA,34987
|
18
18
|
maps4fs/generator/component/base/__init__.py,sha256=zZMLEkGzb4z0xql650gOtGSvcgX58DnJ2yN3vC2daRk,43
|
19
19
|
maps4fs/generator/component/base/component.py,sha256=Vgmdsn1ZC37EwWi4Va4uYVt0RnFLiARTtZ-R5GTSrrM,22877
|
20
|
-
maps4fs/generator/component/base/component_image.py,sha256=
|
20
|
+
maps4fs/generator/component/base/component_image.py,sha256=n_tHyq8J1zWnb1_8O6fhNNd_4Wfj2VU2QqSAEzJXf60,8119
|
21
21
|
maps4fs/generator/component/base/component_mesh.py,sha256=_thzgjJDroMn-9SBsBmAWizcSsnV9U5445SD18Tx1kc,9090
|
22
22
|
maps4fs/generator/component/base/component_xml.py,sha256=MT-VhU2dEckLFxAgmxg6V3gnv11di_94Qq6atfpOLdc,5342
|
23
|
-
maps4fs-2.1.
|
24
|
-
maps4fs-2.1.
|
25
|
-
maps4fs-2.1.
|
26
|
-
maps4fs-2.1.
|
27
|
-
maps4fs-2.1.
|
23
|
+
maps4fs-2.1.3.dist-info/licenses/LICENSE.md,sha256=pTKD_oUexcn-yccFCTrMeLkZy0ifLRa-VNcDLqLZaIw,10749
|
24
|
+
maps4fs-2.1.3.dist-info/METADATA,sha256=qJ4vDMMwhoupUdurZjg1aROKauLLhWLx9845zyynngY,45431
|
25
|
+
maps4fs-2.1.3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
26
|
+
maps4fs-2.1.3.dist-info/top_level.txt,sha256=Ue9DSRlejRQRCaJueB0uLcKrWwsEq9zezfv5dI5mV1M,8
|
27
|
+
maps4fs-2.1.3.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|