maps4fs 2.0.9__py3-none-any.whl → 2.1.1__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/base/component_mesh.py +1 -1
- maps4fs/generator/component/base/component_xml.py +37 -0
- maps4fs/generator/component/config.py +121 -0
- maps4fs/generator/component/i3d.py +91 -11
- maps4fs/generator/game.py +30 -0
- maps4fs/generator/settings.py +4 -0
- {maps4fs-2.0.9.dist-info → maps4fs-2.1.1.dist-info}/METADATA +3 -1
- {maps4fs-2.0.9.dist-info → maps4fs-2.1.1.dist-info}/RECORD +11 -11
- {maps4fs-2.0.9.dist-info → maps4fs-2.1.1.dist-info}/WHEEL +0 -0
- {maps4fs-2.0.9.dist-info → maps4fs-2.1.1.dist-info}/licenses/LICENSE.md +0 -0
- {maps4fs-2.0.9.dist-info → maps4fs-2.1.1.dist-info}/top_level.txt +0 -0
@@ -4,6 +4,7 @@ import os
|
|
4
4
|
from xml.etree import ElementTree as ET
|
5
5
|
|
6
6
|
from maps4fs.generator.component.base.component import Component
|
7
|
+
from maps4fs.generator.settings import Parameters
|
7
8
|
|
8
9
|
|
9
10
|
class XMLComponent(Component):
|
@@ -57,6 +58,20 @@ class XMLComponent(Component):
|
|
57
58
|
|
58
59
|
tree.write(xml_path, encoding="utf-8", xml_declaration=True)
|
59
60
|
|
61
|
+
def get_element_from_tree(self, path: str, xml_path: str | None = None) -> ET.Element | None:
|
62
|
+
"""Finds an element in the XML tree by the path.
|
63
|
+
|
64
|
+
Arguments:
|
65
|
+
path (str): The path to the element.
|
66
|
+
xml_path (str, optional): The path to the XML file. Defaults to None.
|
67
|
+
|
68
|
+
Returns:
|
69
|
+
ET.Element | None: The found element or None if not found.
|
70
|
+
"""
|
71
|
+
tree = self.get_tree(xml_path)
|
72
|
+
root = tree.getroot()
|
73
|
+
return root.find(path) # type: ignore
|
74
|
+
|
60
75
|
def get_and_update_element(self, root: ET.Element, path: str, data: dict[str, str]) -> None:
|
61
76
|
"""Finds the element by the path and updates it with the provided data.
|
62
77
|
|
@@ -106,3 +121,25 @@ class XMLComponent(Component):
|
|
106
121
|
"""
|
107
122
|
element = ET.SubElement(parent, element_name)
|
108
123
|
self.update_element(element, data)
|
124
|
+
|
125
|
+
def get_height_scale(self) -> int:
|
126
|
+
"""Returns the height scale from the I3D file.
|
127
|
+
|
128
|
+
Returns:
|
129
|
+
int: The height scale value.
|
130
|
+
|
131
|
+
Raises:
|
132
|
+
ValueError: If the height scale element is not found in the I3D file.
|
133
|
+
"""
|
134
|
+
height_scale_element = self.get_element_from_tree(
|
135
|
+
path=".//Scene/TerrainTransformGroup",
|
136
|
+
xml_path=self.game.i3d_file_path(self.map_directory),
|
137
|
+
)
|
138
|
+
if height_scale_element is None:
|
139
|
+
raise ValueError("Height scale element not found in the I3D file.")
|
140
|
+
|
141
|
+
height_scale = height_scale_element.get(Parameters.HEIGHT_SCALE)
|
142
|
+
if height_scale is None:
|
143
|
+
raise ValueError("Height scale not found in the I3D file.")
|
144
|
+
|
145
|
+
return int(height_scale)
|
@@ -2,6 +2,10 @@
|
|
2
2
|
|
3
3
|
from __future__ import annotations
|
4
4
|
|
5
|
+
import os
|
6
|
+
|
7
|
+
import cv2
|
8
|
+
|
5
9
|
from maps4fs.generator.component.base.component_xml import XMLComponent
|
6
10
|
|
7
11
|
|
@@ -23,11 +27,15 @@ class Config(XMLComponent):
|
|
23
27
|
def preprocess(self) -> None:
|
24
28
|
"""Gets the path to the map XML file and saves it to the instance variable."""
|
25
29
|
self.xml_path = self.game.map_xml_path(self.map_directory)
|
30
|
+
self.fog_parameters: dict[str, int] = {}
|
26
31
|
|
27
32
|
def process(self) -> None:
|
28
33
|
"""Sets the map size in the map.xml file."""
|
29
34
|
self._set_map_size()
|
30
35
|
|
36
|
+
if self.game.fog_processing:
|
37
|
+
self._adjust_fog()
|
38
|
+
|
31
39
|
def _set_map_size(self) -> None:
|
32
40
|
"""Edits map.xml file to set correct map size."""
|
33
41
|
tree = self.get_tree()
|
@@ -77,6 +85,8 @@ class Config(XMLComponent):
|
|
77
85
|
data = {
|
78
86
|
"Overview": overview_data,
|
79
87
|
}
|
88
|
+
if self.fog_parameters:
|
89
|
+
data["Fog"] = self.fog_parameters # type: ignore
|
80
90
|
|
81
91
|
return data # type: ignore
|
82
92
|
|
@@ -92,3 +102,114 @@ class Config(XMLComponent):
|
|
92
102
|
layers = qgis_layers + qgis_layers_with_margin
|
93
103
|
|
94
104
|
self.create_qgis_scripts(layers)
|
105
|
+
|
106
|
+
def _adjust_fog(self) -> None:
|
107
|
+
"""Adjusts the fog settings in the environment XML file based on the DEM and height scale."""
|
108
|
+
self.logger.debug("Adjusting fog settings based on DEM and height scale...")
|
109
|
+
try:
|
110
|
+
environment_xml_path = self.game.get_environment_xml_path(self.map_directory)
|
111
|
+
except NotImplementedError:
|
112
|
+
self.logger.warning(
|
113
|
+
"Game does not support environment XML file, fog adjustment will not be applied."
|
114
|
+
)
|
115
|
+
return
|
116
|
+
|
117
|
+
if not environment_xml_path or not os.path.isfile(environment_xml_path):
|
118
|
+
self.logger.warning(
|
119
|
+
"Environment XML file not found, fog adjustment will not be applied."
|
120
|
+
)
|
121
|
+
return
|
122
|
+
|
123
|
+
self.logger.debug("Will work with environment XML file: %s", environment_xml_path)
|
124
|
+
|
125
|
+
dem_params = self._get_dem_meter_params()
|
126
|
+
if not dem_params:
|
127
|
+
return
|
128
|
+
maximum_height, minimum_height = dem_params
|
129
|
+
|
130
|
+
tree = self.get_tree(xml_path=environment_xml_path)
|
131
|
+
root = tree.getroot()
|
132
|
+
|
133
|
+
# Find the <latitude>40.6</latitude> element in the XML file.
|
134
|
+
latitude_element = root.find("./latitude") # type: ignore
|
135
|
+
if latitude_element is not None:
|
136
|
+
map_latitude = round(self.map.coordinates[0], 1)
|
137
|
+
latitude_element.text = str(map_latitude)
|
138
|
+
self.logger.debug(
|
139
|
+
"Found latitude element and set it to: %s",
|
140
|
+
latitude_element.text,
|
141
|
+
)
|
142
|
+
|
143
|
+
# The XML file contains 4 <fog> entries in different sections of <weather> representing
|
144
|
+
# different seasons, such as <season name="spring">, <season name="summer">, etc.
|
145
|
+
# We need to find them all and adjust the parameters accordingly.
|
146
|
+
for season in root.findall(".//weather/season"): # type: ignore
|
147
|
+
# Example of the <heightFog> element:
|
148
|
+
# <heightFog>
|
149
|
+
# <groundLevelDensity min="0.05" max="0.2" />
|
150
|
+
# <maxHeight min="420" max="600" />
|
151
|
+
# </heightFog>
|
152
|
+
# We need to adjust the maxheight min and max attributes.
|
153
|
+
max_height_element = season.find("./fog/heightFog/maxHeight")
|
154
|
+
data = {
|
155
|
+
"min": str(minimum_height),
|
156
|
+
"max": str(maximum_height),
|
157
|
+
}
|
158
|
+
self.update_element(max_height_element, data) # type: ignore
|
159
|
+
self.logger.debug(
|
160
|
+
"Adjusted fog settings for season '%s': min=%s, max=%s",
|
161
|
+
season.get("name", "unknown"),
|
162
|
+
minimum_height,
|
163
|
+
maximum_height,
|
164
|
+
)
|
165
|
+
|
166
|
+
self.logger.debug("Fog adjusted and file will be saved to %s", environment_xml_path)
|
167
|
+
self.save_tree(tree, xml_path=environment_xml_path)
|
168
|
+
|
169
|
+
self.fog_parameters = {
|
170
|
+
"minimum_height": minimum_height,
|
171
|
+
"maximum_height": maximum_height,
|
172
|
+
}
|
173
|
+
|
174
|
+
def _get_dem_meter_params(self) -> tuple[int, int] | None:
|
175
|
+
"""Reads the DEM file and returns the maximum and minimum height in meters.
|
176
|
+
|
177
|
+
Returns:
|
178
|
+
tuple[int, int] | None: Maximum and minimum height in meters or None if the DEM file
|
179
|
+
is not found or cannot be read.
|
180
|
+
"""
|
181
|
+
self.logger.debug("Reading DEM meter parameters...")
|
182
|
+
dem_path = self.game.dem_file_path(self.map_directory)
|
183
|
+
if not dem_path or not os.path.isfile(dem_path):
|
184
|
+
self.logger.warning("DEM file not found, fog adjustment will not be applied.")
|
185
|
+
return None
|
186
|
+
|
187
|
+
dem_image = cv2.imread(dem_path, cv2.IMREAD_UNCHANGED)
|
188
|
+
if dem_image is None:
|
189
|
+
self.logger.warning("Failed to read DEM image, fog adjustment will not be applied.")
|
190
|
+
return None
|
191
|
+
dem_maximum_pixel = dem_image.max()
|
192
|
+
dem_minimum_pixel = dem_image.min()
|
193
|
+
|
194
|
+
self.logger.debug(
|
195
|
+
"DEM read successfully. Max pixel: %d, Min pixel: %d",
|
196
|
+
dem_maximum_pixel,
|
197
|
+
dem_minimum_pixel,
|
198
|
+
)
|
199
|
+
|
200
|
+
try:
|
201
|
+
height_scale = self.get_height_scale()
|
202
|
+
except ValueError as e:
|
203
|
+
self.logger.warning("Error getting height scale from I3D file: %s", e)
|
204
|
+
return None
|
205
|
+
self.logger.debug("Height scale from I3D file: %d", height_scale)
|
206
|
+
|
207
|
+
dem_maximum_meter = int(dem_maximum_pixel / height_scale)
|
208
|
+
dem_minimum_meter = int(dem_minimum_pixel / height_scale)
|
209
|
+
self.logger.debug(
|
210
|
+
"DEM maximum height in meters: %d, minimum height in meters: %d",
|
211
|
+
dem_maximum_meter,
|
212
|
+
dem_minimum_meter,
|
213
|
+
)
|
214
|
+
|
215
|
+
return dem_maximum_meter, dem_minimum_meter
|
@@ -5,7 +5,7 @@ from __future__ import annotations
|
|
5
5
|
import json
|
6
6
|
import os
|
7
7
|
from random import choice, randint, uniform
|
8
|
-
from typing import Generator
|
8
|
+
from typing import Any, Generator
|
9
9
|
from xml.etree import ElementTree as ET
|
10
10
|
|
11
11
|
import cv2
|
@@ -48,6 +48,9 @@ class I3d(XMLComponent):
|
|
48
48
|
attribute. If the game does not support I3D files, the attribute is set to None."""
|
49
49
|
self.xml_path = self.game.i3d_file_path(self.map_directory)
|
50
50
|
|
51
|
+
self.forest_info: dict[str, Any] = {}
|
52
|
+
self.field_info: dict[str, Any] = {}
|
53
|
+
|
51
54
|
def process(self) -> None:
|
52
55
|
"""Updates the map I3D file and creates splines in a separate I3D file."""
|
53
56
|
self.update_height_scale()
|
@@ -83,7 +86,7 @@ class I3d(XMLComponent):
|
|
83
86
|
root = tree.getroot()
|
84
87
|
path = ".//Scene/TerrainTransformGroup"
|
85
88
|
|
86
|
-
data = {
|
89
|
+
data = {Parameters.HEIGHT_SCALE: str(value)}
|
87
90
|
|
88
91
|
self.get_and_update_element(root, path, data) # type: ignore
|
89
92
|
self.save_tree(tree) # type: ignore
|
@@ -267,6 +270,8 @@ class I3d(XMLComponent):
|
|
267
270
|
|
268
271
|
node_id = NODE_ID_STARTING_VALUE
|
269
272
|
field_id = 1
|
273
|
+
added_fields = skipped_fields = 0
|
274
|
+
skipped_field_ids: list[int] = []
|
270
275
|
|
271
276
|
for field in tqdm(fields, desc="Adding fields", unit="field"):
|
272
277
|
try:
|
@@ -279,6 +284,7 @@ class I3d(XMLComponent):
|
|
279
284
|
field_id,
|
280
285
|
e,
|
281
286
|
)
|
287
|
+
skipped_fields += 1
|
282
288
|
continue
|
283
289
|
|
284
290
|
field_ccs = [self.top_left_coordinates_to_center(point) for point in fitted_field]
|
@@ -297,6 +303,11 @@ class I3d(XMLComponent):
|
|
297
303
|
|
298
304
|
node_id += 1
|
299
305
|
field_id += 1
|
306
|
+
added_fields += 1
|
307
|
+
|
308
|
+
self.field_info["added_fields"] = added_fields
|
309
|
+
self.field_info["skipped_fields"] = skipped_fields
|
310
|
+
self.field_info["skipped_field_ids"] = skipped_field_ids
|
300
311
|
|
301
312
|
self.save_tree(tree)
|
302
313
|
|
@@ -504,6 +515,8 @@ class I3d(XMLComponent):
|
|
504
515
|
|
505
516
|
node_id = TREE_NODE_ID_STARTING_VALUE
|
506
517
|
|
518
|
+
tree_count = 0
|
519
|
+
|
507
520
|
for forest_layer in forest_layers:
|
508
521
|
weights_directory = self.game.weights_dir_path(self.map_directory)
|
509
522
|
forest_image_path = forest_layer.get_preview_or_path(weights_directory)
|
@@ -542,12 +555,17 @@ class I3d(XMLComponent):
|
|
542
555
|
)
|
543
556
|
|
544
557
|
forest_image = cv2.imread(forest_image_path, cv2.IMREAD_UNCHANGED)
|
545
|
-
|
546
|
-
|
547
|
-
|
558
|
+
|
559
|
+
step = self.get_step_by_limit(
|
560
|
+
forest_image, # type: ignore
|
561
|
+
self.map.i3d_settings.tree_limit,
|
562
|
+
self.map.i3d_settings.forest_density,
|
563
|
+
)
|
564
|
+
|
565
|
+
for x, y in self.non_empty_pixels(forest_image, step=step): # type: ignore
|
548
566
|
shifted_x, shifted_y = self.randomize_coordinates(
|
549
567
|
(x, y),
|
550
|
-
|
568
|
+
step,
|
551
569
|
self.map.i3d_settings.trees_relative_shift,
|
552
570
|
)
|
553
571
|
|
@@ -573,9 +591,16 @@ class I3d(XMLComponent):
|
|
573
591
|
}
|
574
592
|
trees_node.append(self.create_element("ReferenceNode", data))
|
575
593
|
|
594
|
+
tree_count += 1
|
595
|
+
|
576
596
|
scene_node.append(trees_node)
|
577
597
|
self.save_tree(tree)
|
578
598
|
|
599
|
+
self.forest_info["tree_count"] = tree_count
|
600
|
+
self.forest_info["tree_limit"] = self.map.i3d_settings.tree_limit
|
601
|
+
self.forest_info["initial_step"] = self.map.i3d_settings.forest_density
|
602
|
+
self.forest_info["actual_step"] = step
|
603
|
+
|
579
604
|
self.assets.forests = self.xml_path
|
580
605
|
|
581
606
|
@staticmethod
|
@@ -606,19 +631,61 @@ class I3d(XMLComponent):
|
|
606
631
|
image: np.ndarray, step: int = 1
|
607
632
|
) -> Generator[tuple[int, int], None, None]:
|
608
633
|
"""Receives numpy array, which represents single-channeled image of uint8 type.
|
609
|
-
Yield coordinates of non-empty pixels (pixels with value greater than 0).
|
634
|
+
Yield coordinates of non-empty pixels (pixels with value greater than 0), sampling about 1/step of them.
|
610
635
|
|
611
636
|
Arguments:
|
612
637
|
image (np.ndarray): The image to get non-empty pixels from.
|
613
|
-
step (int, optional): The step to
|
638
|
+
step (int, optional): The step to sample non-empty pixels. Defaults to 1.
|
614
639
|
|
615
640
|
Yields:
|
616
641
|
tuple[int, int]: The coordinates of non-empty pixels.
|
617
642
|
"""
|
618
|
-
|
619
|
-
|
643
|
+
count = 0
|
644
|
+
for y, row in enumerate(image):
|
645
|
+
for x, value in enumerate(row):
|
620
646
|
if value > 0:
|
621
|
-
|
647
|
+
if count % step == 0:
|
648
|
+
yield x, y
|
649
|
+
count += 1
|
650
|
+
|
651
|
+
@staticmethod
|
652
|
+
def non_empty_pixels_count(image: np.ndarray) -> int:
|
653
|
+
"""Counts the number of non-empty pixels in the image.
|
654
|
+
|
655
|
+
Arguments:
|
656
|
+
image (np.ndarray): The image to count non-empty pixels in.
|
657
|
+
|
658
|
+
Returns:
|
659
|
+
int: The number of non-empty pixels in the image.
|
660
|
+
"""
|
661
|
+
result = np.count_nonzero(image > 0)
|
662
|
+
return result
|
663
|
+
|
664
|
+
def get_step_by_limit(
|
665
|
+
self, image: np.ndarray, limit: int, current_step: int | None = None
|
666
|
+
) -> int:
|
667
|
+
"""Calculates the step size for iterating through the image based on the limit based
|
668
|
+
on the number of non-empty pixels in the image.
|
669
|
+
|
670
|
+
Arguments:
|
671
|
+
image (np.ndarray): The image to calculate the step size for.
|
672
|
+
limit (int): The maximum number of non-empty pixels to process.
|
673
|
+
current_step (int | None, optional): The current step size. If provided, the method
|
674
|
+
will return the maximum of the recommended step and the current step.
|
675
|
+
|
676
|
+
Returns:
|
677
|
+
int: The recommended step size for iterating through the image.
|
678
|
+
"""
|
679
|
+
available_tree_count = self.non_empty_pixels_count(image)
|
680
|
+
self.forest_info["available_tree_count"] = available_tree_count
|
681
|
+
if limit <= 0 or available_tree_count <= limit:
|
682
|
+
recommended_step = 1
|
683
|
+
else:
|
684
|
+
recommended_step = int(available_tree_count / limit)
|
685
|
+
|
686
|
+
self.forest_info["step_by_limit"] = recommended_step
|
687
|
+
|
688
|
+
return recommended_step if not current_step else max(recommended_step, current_step)
|
622
689
|
|
623
690
|
def get_not_resized_dem(self) -> np.ndarray | None:
|
624
691
|
"""Reads the not resized DEM image from the background component.
|
@@ -638,3 +705,16 @@ class I3d(XMLComponent):
|
|
638
705
|
not_resized_dem = cv2.imread(background_component.not_resized_path, cv2.IMREAD_UNCHANGED)
|
639
706
|
|
640
707
|
return not_resized_dem
|
708
|
+
|
709
|
+
def info_sequence(self) -> dict[str, dict[str, str | float | int]]:
|
710
|
+
"""Returns information about the component.
|
711
|
+
|
712
|
+
Returns:
|
713
|
+
dict[str, dict[str, str | float | int]]: Information about the component.
|
714
|
+
"""
|
715
|
+
data = {
|
716
|
+
"Forests": self.forest_info,
|
717
|
+
"Fields": self.field_info,
|
718
|
+
}
|
719
|
+
|
720
|
+
return data
|
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
|
+
_fog_processing: bool = True
|
43
44
|
_dissolve: bool = True
|
44
45
|
|
45
46
|
# Order matters! Some components depend on others.
|
@@ -189,6 +190,16 @@ class Game:
|
|
189
190
|
str: The path to the farmlands xml file."""
|
190
191
|
raise NotImplementedError
|
191
192
|
|
193
|
+
def get_environment_xml_path(self, map_directory: str) -> str:
|
194
|
+
"""Returns the path to the environment xml file.
|
195
|
+
|
196
|
+
Arguments:
|
197
|
+
map_directory (str): The path to the map directory.
|
198
|
+
|
199
|
+
Returns:
|
200
|
+
str: The path to the environment xml file."""
|
201
|
+
raise NotImplementedError
|
202
|
+
|
192
203
|
def i3d_file_path(self, map_directory: str) -> str:
|
193
204
|
"""Returns the path to the i3d file.
|
194
205
|
|
@@ -207,6 +218,14 @@ class Game:
|
|
207
218
|
bool: True if the i3d file should be processed, False otherwise."""
|
208
219
|
return self._i3d_processing
|
209
220
|
|
221
|
+
@property
|
222
|
+
def fog_processing(self) -> bool:
|
223
|
+
"""Returns whether the fog should be processed.
|
224
|
+
|
225
|
+
Returns:
|
226
|
+
bool: True if the fog should be processed, False otherwise."""
|
227
|
+
return self._fog_processing
|
228
|
+
|
210
229
|
@property
|
211
230
|
def plants_processing(self) -> bool:
|
212
231
|
"""Returns whether the plants should be processed.
|
@@ -250,6 +269,7 @@ class FS22(Game):
|
|
250
269
|
_map_template_path = os.path.join(working_directory, "data", "fs22-map-template.zip")
|
251
270
|
_texture_schema = os.path.join(working_directory, "data", "fs22-texture-schema.json")
|
252
271
|
_i3d_processing = False
|
272
|
+
_fog_processing = False
|
253
273
|
_plants_processing = False
|
254
274
|
_dissolve = False
|
255
275
|
|
@@ -345,3 +365,13 @@ class FS25(Game):
|
|
345
365
|
Returns:
|
346
366
|
str: The path to the farmlands xml file."""
|
347
367
|
return os.path.join(map_directory, "map", "config", "farmlands.xml")
|
368
|
+
|
369
|
+
def get_environment_xml_path(self, map_directory: str) -> str:
|
370
|
+
"""Returns the path to the environment xml file.
|
371
|
+
|
372
|
+
Arguments:
|
373
|
+
map_directory (str): The path to the map directory.
|
374
|
+
|
375
|
+
Returns:
|
376
|
+
str: The path to the environment xml file."""
|
377
|
+
return os.path.join(map_directory, "map", "config", "environment.xml")
|
maps4fs/generator/settings.py
CHANGED
@@ -40,6 +40,8 @@ class Parameters:
|
|
40
40
|
|
41
41
|
WATER_ADD_WIDTH = 2
|
42
42
|
|
43
|
+
HEIGHT_SCALE = "heightScale"
|
44
|
+
|
43
45
|
|
44
46
|
class SharedSettings(BaseModel):
|
45
47
|
"""Represents the shared settings for all components."""
|
@@ -201,6 +203,7 @@ class I3DSettings(SettingsModel):
|
|
201
203
|
Attributes:
|
202
204
|
add_trees (bool): add trees to the map.
|
203
205
|
forest_density (int): density of the forest (distance between trees).
|
206
|
+
tree_limit (int): maximum number of trees to be added to the map.
|
204
207
|
trees_relative_shift (int): relative shift of the trees.
|
205
208
|
spline_density (int): the number of extra points that will be added between each two
|
206
209
|
existing points.
|
@@ -210,6 +213,7 @@ class I3DSettings(SettingsModel):
|
|
210
213
|
|
211
214
|
add_trees: bool = True
|
212
215
|
forest_density: int = 10
|
216
|
+
tree_limit: int = 0
|
213
217
|
trees_relative_shift: int = 20
|
214
218
|
|
215
219
|
spline_density: int = 2
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: maps4fs
|
3
|
-
Version: 2.
|
3
|
+
Version: 2.1.1
|
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
|
@@ -650,6 +650,8 @@ You can also apply some advanced settings to the map generation process.<br>
|
|
650
650
|
|
651
651
|
- Forest density - the density of the forest in meters. The lower the value, the lower the distance between the trees, which makes the forest denser. Note, that low values will lead to enormous number of trees, which may cause the Giants Editor to crash or lead to performance issues. By default, it's set to 10.
|
652
652
|
|
653
|
+
- Tree limit - this value will be used to adjust the forest density value. For example, if it's possible to place 100000 trees from OSM data, and the forest density is set to 10, the expected number of trees on map will be 10000. At the same time, if you set the tree limit to 5000, the forest density will be adjusted to 20, which means that the distance between the trees will be doubled. This value is useful to prevent the Giants Editor from crashing due to too many trees on the map. By default, it's set to 0, which means that it's disabled and will use the forest density value as is. Note, that it will not lead to the exact number of trees, but will adjust the forest density value to fit the tree limit so the resulting number of trees will be more or less equal to the tree limit value.
|
654
|
+
|
653
655
|
- Trees relative shift - represents the maximum possible shift of the tree from it's original position in percents of the forest density value. The higher the value, the more the trees will be shifted from their original positions. Warning: higher values can lead to overlapping trees.
|
654
656
|
|
655
657
|
- Splines density - number of points, which will be added (interpolate) between each pair of existing points. The higher the value, the denser the spline will be. It can smooth the splines, but high values can in opposite make the splines look unnatural.
|
@@ -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=Nl8xqEwW-E04l2D3e9ff7kkzPcolr1ndnXxQJlLMBDM,12627
|
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=OXgCjpmcqvTENRcKKYcEhES3GD3KPpvrbeJDv8g25yY,7676
|
8
8
|
maps4fs/generator/statistics.py,sha256=aynS3zbAtiwnU_YLKHPTiiaKW98_suvQUhy1SGBA6mc,2448
|
9
9
|
maps4fs/generator/component/__init__.py,sha256=s01yVVVi8R2xxNvflu2D6wTd9I_g73AMM2x7vAC7GX4,490
|
10
10
|
maps4fs/generator/component/background.py,sha256=wqTgndTfRIGlLMpMXqJMw_6eVdH1JtMei46ZIXlT9X4,29480
|
11
|
-
maps4fs/generator/component/config.py,sha256=
|
11
|
+
maps4fs/generator/component/config.py,sha256=uL76h9UwyhZKZmbxz0mBmWtEPN6qYay4epTEqqtej60,8601
|
12
12
|
maps4fs/generator/component/dem.py,sha256=SH_2Zu5O4dhWtZeOkCwzDF4RU04XhTdpGFYaRYJkdjc,11905
|
13
13
|
maps4fs/generator/component/grle.py,sha256=8K32pC_ar9CR6p0EhCe2X--wEoIxFzJCPcN9ydHQ1LE,19747
|
14
|
-
maps4fs/generator/component/i3d.py,sha256=
|
14
|
+
maps4fs/generator/component/i3d.py,sha256=L-QAbr3Z7Ye5N0BeS_qvY9bqYxYs0eVnRCGWp77etrE,26693
|
15
15
|
maps4fs/generator/component/layer.py,sha256=-br4gAGcGeBNb3ldch9XFEK0lhXqb1IbArhFB5Owu54,6186
|
16
16
|
maps4fs/generator/component/satellite.py,sha256=OsxoNOCgkUtRzL7Geuqubsf6uoKXAIN8jQvrJ7IFeAI,4958
|
17
17
|
maps4fs/generator/component/texture.py,sha256=krtvOS0hH8BTzfxd2jsTo3bIJYRkIVbaz6FGhWY8L1o,33921
|
18
18
|
maps4fs/generator/component/base/__init__.py,sha256=zZMLEkGzb4z0xql650gOtGSvcgX58DnJ2yN3vC2daRk,43
|
19
19
|
maps4fs/generator/component/base/component.py,sha256=Vgmdsn1ZC37EwWi4Va4uYVt0RnFLiARTtZ-R5GTSrrM,22877
|
20
20
|
maps4fs/generator/component/base/component_image.py,sha256=2NYJgCU8deHl7O2FYFYk38WKZVJygFoc2gjBXwH6vjM,5970
|
21
|
-
maps4fs/generator/component/base/component_mesh.py,sha256=
|
22
|
-
maps4fs/generator/component/base/component_xml.py,sha256=
|
23
|
-
maps4fs-2.
|
24
|
-
maps4fs-2.
|
25
|
-
maps4fs-2.
|
26
|
-
maps4fs-2.
|
27
|
-
maps4fs-2.
|
21
|
+
maps4fs/generator/component/base/component_mesh.py,sha256=_thzgjJDroMn-9SBsBmAWizcSsnV9U5445SD18Tx1kc,9090
|
22
|
+
maps4fs/generator/component/base/component_xml.py,sha256=MT-VhU2dEckLFxAgmxg6V3gnv11di_94Qq6atfpOLdc,5342
|
23
|
+
maps4fs-2.1.1.dist-info/licenses/LICENSE.md,sha256=pTKD_oUexcn-yccFCTrMeLkZy0ifLRa-VNcDLqLZaIw,10749
|
24
|
+
maps4fs-2.1.1.dist-info/METADATA,sha256=uHY1Ap5uahWTaFPIAOulIBz754lJ7Ga4S4RJ6ZOAUpI,44934
|
25
|
+
maps4fs-2.1.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
26
|
+
maps4fs-2.1.1.dist-info/top_level.txt,sha256=Ue9DSRlejRQRCaJueB0uLcKrWwsEq9zezfv5dI5mV1M,8
|
27
|
+
maps4fs-2.1.1.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|